-
-
Notifications
You must be signed in to change notification settings - Fork 34.4k
Move LIFX to aiolifx for driving the bulbs #6584
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
Conversation
@amelchio, thanks for your PR! By analyzing the history of the files in this pull request, we identified @fabaff, @kitcorey and @BillyNate to be potential reviewers. |
if fade < BULB_LATENCY: | ||
self.set_power(0) | ||
|
||
def got_color(self,device,msg): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing whitespace after ','
else: | ||
kelvin = self._kel | ||
|
||
hsbk = [ hue, saturation, brightness, kelvin ] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
whitespace after '['
whitespace before ']'
def probe(self, address=None): | ||
"""Probe the light.""" | ||
self._liffylights.probe(address) | ||
def unregister(self,device): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing whitespace after ','
self.hass = hass | ||
self.async_add_devices = async_add_devices | ||
|
||
def register(self,device): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing whitespace after ','
self.set_power(0) | ||
|
||
def got_color(self, device, msg): | ||
"""Callback that gets current power/color status""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
End with a period.
Sorry for multiple fixups. I don't have a proper local environment yet. I need help on the |
Remove the broadcast configuration variable.
@amelchio, I've been looking over this code and testing it on my own and ran into a few problems.
|
Also, there is a potential problem I just realized. What happens when the bulb is unavailable and automations try and control the bulbs? Do the automations outright fail, or does the lifx component soft fail and allow the rest of the automation to continue. |
Also, we don't need to request an acknowledgement from the bulbs if we're not using it for anything. So on the aiolifx side we should be able to use fire and forget since the component is waiting a predefined interval, then checking the light state. |
@keatontaylor Thank you for looking at this!
|
@amelchio I believe I discovered why it is taking so long for re-registering after bulbs are inactive. I am running the docker HA image and the component needed to be set to listen to incoming broadcasts packets from the right subnet, but this isn't easily configurable. So those of us using the docker image will need that added. |
@keatontaylor I admit ignorance on Docker so I must get it set up to test that. I assume that this is Docker on Linux? Also, how long is "so long"? |
@keatontaylor I have been testing this PR for some time on RPi and it is significantly more responsive compared to the current Lifx component. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This already looks very good!
Just a few minor comments.
|
||
lifx_library.probe() | ||
hass.loop.create_task(coro) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use hass.async_add_job
(it does the same except that it allows us to only finish starting up when this task is done)
self.hass = hass | ||
self.async_add_devices = async_add_devices | ||
|
||
def register(self, device): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please mark these classes with @callback
since they are being called from inside the event loop. This will help readers of this code understand what is going on.
entity = self.entities[device.mac_addr] | ||
_LOGGER.debug("%s register AGAIN", entity.ipaddr) | ||
entity.available = True | ||
entity.schedule_update_ha_state() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are already in the event loop. Please use self.hass.async_add_job(entity.async_update_ha_state())
def poll(self, now): | ||
"""Polling for the light.""" | ||
self.probe() | ||
def ready(self, device, msg): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a @callback
annotation.
def probe(self, address=None): | ||
"""Probe the light.""" | ||
self._liffylights.probe(address) | ||
def unregister(self, device): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a @callback
annotation.
_LOGGER.debug("%s unregister", entity.ipaddr) | ||
entity.available = False | ||
entity.updated_event.set() | ||
entity.schedule_update_ha_state() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are already in the event loop. Please use self.hass.async_add_job(entity.async_update_ha_state())
def should_poll(self): | ||
"""No polling needed for LIFX light.""" | ||
return False | ||
def available(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of being all this fancy, you can also just have an available
property on the entity (defined in __init__
). No need abstracting it in a setter.
def turn_off(self, **kwargs): | ||
self.update_later(0) | ||
if fade < BULB_LATENCY: | ||
self.set_power(1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For other reviewers:
This is not doing I/O in the event loop. Checked it, aiolifx will schedule a task on the event loop when we call this. https://github.com/frawau/aiolifx/blob/master/aiolifx/aiolifx34.py#L143
@balloob Thanks for the review and the intro text in particular, that made my day :-) I now added the But I cannot figure out how to get rid of the fancy |
Oh my bad about |
Remove the broadcast configuration variable.
After updating to the version with this commit, my LiFX bulb no longer appears. It worked flawlessly in the previous version |
@kbickar I'm sorry to hear that but thanks for reporting it. Can you tell me a bit about your bulb and HA setup so we can locate the problem? |
@amelchio I have a LIFX Original - 02d279 v2.1 and the bulb is defined in my main configuration file as: |
@kbickar Okay, because of the
to
(or whatever your actual broadcast IP address is) and restart HA? |
Experiencing a similar issue, not all bulbs are showing up when HA is restarted. Changed the broadcast as you suggested without success. |
This change is a big rework, so some fallout was expected. I am, however, committed to fix these things up so the change ends up a net improvement. It is probably best to file individual issues for the regressions that you experience so we can go through them one by one and see if they each have the same cause. |
As a datapoint, it's working very well for me. Best to file individual bugs, so that these issues can be tracked. |
Setting the broadcast IP did not help. Additionally, I was watching in wireshark and I don't see any traffic going to either the IP of the bulb (the bulb occasionally broadcasts itself though), or to the UDP port 56700. Are there any changes that might be needed in the configuration file? |
@kbickar On UDP port 56700 you should at least see a broadcast every 180 seconds, probing for new bulbs. So if you do not see that, maybe the LIFX component is not even loaded. I am actually not sure how to verify that (I would just search the logfile for "lifx" and hope it has some relevant message). For the configuration, Which OS are you on? light 2:
- platform: lifx
server: 192.168.1.39 |
I have multiple network interfaces for VMs running on Windows 10. Does asyncio support windows? |
@kbickar oh my, it seems you are right. That will take a little while to sort out, so you may want to revert to the previous lifx.py for now. |
Oddly my issue was fixed not by changing the broadcast, but instead by power cycling the bulbs. Not sure why, but I've experienced these same issues before with the LIFX iOS app. |
Description:
This is more or less a rewrite of the LIFX component, migrating it to aiolifx. This makes it async, fixes all reported issues (and some unreported ones) and lays the ground for new features like support for the
flash
parameter.The only tricky thing is that LIFX bulbs will report the old state for a short while after setting a new state. I therefore pause update requests to the bulb for 0.5s after setting a state.
The rework has been tested in #6435 but I am new to Python as well as Home Assistant so I expect some issues with implementation details.
Related issue (if applicable): fixes #3604, #4589, #5158, #6245, #6435
Pull request in home-assistant.github.io with documentation (if applicable): home-assistant/home-assistant.io#2259
Checklist:
If user exposed functionality or configuration variables are added/changed:
If the code communicates with devices, web services, or third-party tools:
tox
run successfully.REQUIREMENTS
variable ([example][ex-requir]).requirements_all.txt
by runningscript/gen_requirements_all.py
.