mclarkk / lifxlan

Python library for accessing LIFX devices locally using the official LIFX LAN protocol.
MIT License
503 stars 115 forks source link

Lights failing to reply with acknowledgement. Can we get max retries and timeout as arguments? #139

Open aredon opened 4 years ago

aredon commented 4 years ago

I have a single A12 bulb and a Z strip that I am sending commands from the same script. I am running a script I call sunrise.py that slowly fades the lights in over a series of color commands. I'm very happy with it right now but recently it has stopped being reliable. At first I had issues with discovery randomly not finding lights but I mostly resolved that by just trying discovery again.

At the moment the lights (and by lights I mean, usually one gets stuck and the other keeps going) get through "initialization" and "phase 1" but fail to reach "phase 2". I have checked my network and as far as I can tell there's been no packets dropped or disconnections. Mind you this is completely random and most of the time it works fine.

I have also noticed that I can duplicate the issue (kind of - for both lights at the same time) by turning the router off when the script reaches "phase 1" and then I get acknowledgement errors. Is there a way I can wait for an acknowledgement and try to send the command again a certain number of times? I feel like that would drastically improve reliability. At the moment the script just ends when acknowledgement fails to come back so I added rapid flags to "hall light" because "window sunshine" is the more important one in this script.

#Import the goodies
from lifxlan import *
from time import sleep
import sys

#Set how long this whole thing 
if len(sys.argv) > 1:
    wake_time = float(sys.argv[1]) # time in minutes
else:
    wake_time = 80

print wake_time

# get devices
lifx = LifxLAN(2)
#devices = lifx.get_lights()
light = lifx.get_device_by_name("Window Sunshine")
light2 = lifx.get_device_by_name("Hall Light")

#make sure devices actually got listed
if not light:
    print "Failed to find Window Sunshine, retrying..."
    light = lifx.get_device_by_name("Window Sunshine")
if not light2:
    print "Failed to find Hall Light, retrying..."
    light2 = lifx.get_device_by_name("Hall Light")

#let the log know we were successful
if light:
    print "Window Sunshine Found"
if light2:
    print "Hall Light Found"
#if len(devices) > 0:
#   light = devices[0]
#else:
#   devices = lifx.get_lights()
#   light = devices[0]

#Initialization
if light:
    light.set_zone_colors([(0, 0, 0, 0), (6553, 65535, 1965, 4000), (0, 0, 0, 0), (50243, 24247, 1966, 6500), (50243, 24247, 1966, 6500), (50243, 24247, 1966,6500), (50243, 24247, 1966, 6500), (0, 0, 0, 0), (0, 0, 0, 0), (50243, 24247, 1966, 6500), (50243, 24247, 1966, 6500), (50243, 24247, 1966, 6500), (50243, 24247, 1966, 6500), (0, 0, 0, 0), (6553, 65535, 1965, 4000), (0, 0, 0, 0)],1)
    light.set_power(True,10000,1)
if light2:
    light2.set_color((50243, 24247, 1966, 6500),1,1)
    light2.set_power(True,10000,1)
sleep(10) #need five seconds of sleep for the five second power-on
sleep(wake_time * 60 * .15) ##lets hold this color for 1/6th of the time
print "Phase 1..."

#fade to a shade of white so that when brightness goes up we dont have extremely bright purples 'n shit
#lets take 1/4 of the time to do this
step1 = (wake_time * 60 * .25)
#light.set_color([6400,0,(.18*65535),9000], (step1*1000), 1)
if light:
    light.set_zone_colors([(0, 0, 0, 0), (6400, 0, 11796, 9000), (0, 0, 0, 0), (6400, 0, 11796, 9000), (6400, 0, 11796, 9000), (6400, 0, 11796, 9000), (6400, 0, 11796, 9000), (182, 0, 0, 3500), (182, 0, 0, 3500), (6400, 0, 11796, 9000), (6400, 0, 11796, 9000), (6400, 0, 11796, 9000), (6400, 0, 11796, 9000), (0, 0, 0, 0), (6400, 0, 11796, 9000), (182, 0, 0, 3500)],(step1*1000))
if light2:
    light2.set_color((6400, 0, 11796, 9000),(step1*1000),1)
sleep(step1)
print "Phase 2..."

#use the remaining time to fade up to the color we want, add a little extra so it's not full blast at wakeup time
step2 = (wake_time * 60 * .6) + (wake_time * 60 * .15)
if light:
    light.set_color([6400,0,65535,6500], (step2*1000))
if light2:
    light2.set_color([6400,0,65535,6500], (step2*1000),1)
sleep(step2)

#keepalive time after waking up
sleep((wake_time * 60 * .5))
if light:
    light.set_power(False,20000,1)
if light2:
    light2.set_power(False,20000,1)
aredon commented 4 years ago
Phase 2...
2020-02-01 06:12:27.644653
Traceback (most recent call last):
  File "/etc/motioneye/sunrise.py", line 77, in <module>
    light.set_color([6400,0,65535,6500], (step2*1000))
  File "/usr/local/lib/python2.7/site-packages/lifxlan/light.py", line 82, in set_color
    self.req_with_ack(LightSetColor, {"color": color, "duration": duration})
  File "/usr/local/lib/python2.7/site-packages/lifxlan/device.py", line 477, in req_with_ack
    self.req_with_resp(msg_type, Acknowledgement, payload, timeout_secs, max_attempts)
  File "/usr/local/lib/python2.7/site-packages/lifxlan/device.py", line 526, in req_with_resp
    raise WorkflowException("WorkflowException: Did not receive {} from {} (Name: {}) in response to {}".format(str(response_type), str(self.mac_addr), str(self.label), str(msg_type)))
lifxlan.errors.WorkflowException: WorkflowException: Did not receive [<class 'lifxlan.msgtypes.Acknowledgement'>] from [MAC ADDRESS] (Name: None) in response to <class 'lifxlan.msgtypes.LightSetColor'>
aredon commented 4 years ago

Having been monitoring this issue for some time now I can safely say:

I'm not sure how I can tell the difference between a packet failing to transmit and just timing out but I would like to be able to modify timeout_secs & max _attempts. I suspect there is a timeout issue here.