delfick / photons

Python3.6+ asyncio framework for interacting with LIFX devices
https://photons.delfick.com
MIT License
73 stars 6 forks source link

CPU Usage #100

Closed illuzn closed 8 months ago

illuzn commented 2 years ago

Firstly, thanks for this amazing software... my programming knowledge is pretty darn basic but using photons interactor allowed me to get my switches working within homeassistant.io

My use case is as follows:

As far as I can tell using timestamps in node-red the response is fairly quick at around 0.1 to 0.2s.

However, accordance to glances, photons interactor is averaging around 30% CPU usage on an RPi400. Now granted an RPi400 doesn't have a fast processor and polling the lights every second isn't exactly the ideal way to do this - however, am I out of my mind in thinking this level of CPU usage is pretty high? It would mean if I added another 6 or so switches I would saturate my cpu.

The logs in photons interactor show nothing untoward besides my 7 query requests and the 7 subsequent responses per second. There doesn't seem to be a way to change the log level in photons interactor so I can't diagnose this further.

delfick commented 2 years ago

Hello!

Firstly, thanks for this amazing software

:)

using photons interactor allowed me to get my switches working within homeassistant.io

nice!

7 query requests a second

I doubt you're using your switches that often :p

The implementation of the LIFX protocol in Photons is optimised for reading efficiency (historical reason related to when I was building photons at LIFX) and optimised for programmer friendlness.

That part was largely designed when I was hypomanic (before I really understood what those episodes were) and still trying to figure out how the protocol actually worked (back in 2016) and it's evolution has been very organic. It's one of the things I really want to fix when I have time (not something I've had much of since late 2020 cause my mental health tanked for quite a while there).

I did start a branch at one point https://github.com/delfick/photons/tree/protocol/rewrite but it has problems and is somehow even more inefficient hahahha

This is why the tile animations have their own hand rolled implementation of the State64 message so that it can send those fast enough for animations to work on an rPi.

So in terms of your problem, I have two questions

  1. How and when is your code creating the messages

    • edit: actually, if you're sending over http to photons interactor, then you don't really have an opportunity for tricks that make the creation of messages more cached. Perhaps we can make an endpoint that can do that based on your answer to question 2
  2. What do you do with the information those replies give you?

illuzn commented 2 years ago

Thanks for the prompt response.

I can't say enough how useful your software has been (at least until your former company decides to roll out proper switch support in its system) - how a button on one switch cannot address a relay on another switch (as opposed to the whole switch) is beyond me. Just to emphasise this you should know that the limit of my programming know is BASIC (not vb) from when I was a kid, some VB for Applications (MS Office) because I was a lazy high schooler and some c#, VB.NET and javascript from uni. Thankfully, your documentation is comprehensive and allowed even a bumbling idiot like me to get things working.

Also, I understand your mental health issues at least a little - I have had a few myself which have come to the forefront because of the COVID situation in Australia. Stay strong and I'm not at all trying to put any pressure on you - as I said, the software is awesome as is.

  1. I'm using an injector in Node-Red to poll the switch every second to see if a relay has been turned on/ off. Yes, I am using the http method so there isn't really the opportunity to cache the message. The only thing I might be able to do is poll all 7 relays using 1 HTTP PUT request (i.e. instead of querying each relay in each switch seperately doing something along the lines of: { "command": "query", "args": { "matcher": { "cap": "buttons" #I only care about relays }, "pkt_type": "GetRPower", "pkt_args": { "relay_index": "0", "1", "2", "3" } } }

From, what I gather from the protocol API, I don't think I am allowed to specify no relays (to query them all) or more than one. I'm also not sure how much cpu doing it this way would help.

  1. I'm using that information to populate the state of the light in homeassistant.io. Obviously polling every 2 seconds instead of 1 (for example) reduces CPU load but this has reducing returns and also makes the interface less responsive for me. From what I gather there is no way to "subscribe" to state updates of lights or relays through the HTTP interface.
delfick commented 2 years ago

how a button on one switch cannot address a relay on another switch (as opposed to the whole switch) is beyond me

we didn't get quite this done before we left, I have no visibility on what happened after that point.

forefront because of the COVID situation in Australia

Yeah, lockdowns certainly didn't help. Better than if we didn't do that, as proven by what now would look like if we didn't have vaccines

From, what I gather from the protocol API, I don't think I am allowed to specify no relays

It is indeed annoying that only GetRPower, SetRPower and StateRPower are public

I'm using that information to populate the state of the light in homeassistant.io.

how do you use that information though?

From what I gather there is no way to "subscribe" to state updates of lights or relays through the HTTP interface.

An endpoint can be made that you subscribe to via websockets and then photons interactor can poll and send updates through the websocket. I assume node-red would be able to do websockets, but I wouldn't have time to help with figuring out that part.

delfick commented 2 years ago

also another part of the CPU usage comes from a bug in how I use tornado where it will serialise and deserialise the json in the request and response multiple times. When I find the time to finish my migration to sanic instead of tornado that bug should go away.

illuzn commented 2 years ago

how do you use that information though?

I use it to set switches on and off in homeassistant otherwise if somebody physically pushes the wall button (or uses any other method than homeassistant) to change lights then it's not reflected in app.

From what I gather there is no way to "subscribe" to state updates of lights or relays through the HTTP interface.

An endpoint can be made that you subscribe to via websockets and then photons interactor can poll and send updates through the websocket. I assume node-red would be able to do websockets, but I wouldn't have time to help with figuring out that part.

Correct me if I'm wrong this would remove some node-red overhead but since you are still polling for updates in photons interactor it would still be using quite a lot of cpu

delfick commented 2 years ago

So you need that information so home assistant can perform a toggle or you just want to know what the current state is?

A toggle can be performed in interactor so it's the one responsible for knowing what current state is.

And I wonder if every minute is enough for status with a refresh button in home assistant to sync if you feel it's not correct.

a websocket would remove the http overhead (especially considering the bug I briefly mentioned) and being inside a loop in interactor and I can reduce a lot of the effort it takes to create the bytes that are sent to the devices.

On Sun, 23 Jan 2022, 7:30 pm illuzn, @.***> wrote:

how do you use that information though?

I use it to set switches on and off in homeassistant otherwise if somebody physically pushes the wall button (or uses any other method than homeassistant) to change lights then it's not reflected in app.

From what I gather there is no way to "subscribe" to state updates of lights or relays through the HTTP interface.

An endpoint can be made that you subscribe to via websockets and then photons interactor can poll and send updates through the websocket. I assume node-red would be able to do websockets, but I wouldn't have time to help with figuring out that part.

Correct me if I'm wrong this would remove some node-red overhead but since you are still polling for updates in photons interactor it would still be using quite a lot of cpu

— Reply to this email directly, view it on GitHub https://github.com/delfick/photons/issues/100#issuecomment-1019437789, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAA2V5PDARFI7FEJIBXWRRLUXO4DJANCNFSM5MMKYPFA . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

You are receiving this because you commented.Message ID: @.***>

Djelibeybi commented 2 years ago

For perspective, I've now switched my switches (see what I did there?) to poll for status once a minute, because folks in my house don't use the physical switches any more and if a guest does, then having things align themselves within the next minute is an acceptable user experience for me.

However, I'm currently in the middle of writing a new LIFX integration for Home Assitant powered by Photons, so switch support should become much easier. Maybe. I'm having to learn a lot of Python, so it's going slow. But I'm making progress. As of this weekend, it actually discovers and can control bulbs! Woo!

illuzn commented 2 years ago

Maybe once every few seconds is fine. I mean you are likely to want to turn a light off after its been turned on for a minute (e.g. wallking down a hallway) but you aren't turning a light of 5 s after right. Will be waiting for your new integration Djelibeybi.

Djelibeybi commented 2 years ago

Whatever period you chose, remember to stagger the requests so that Interactor is only dealing with one at a time. That should lower the overall CPU usage during updates as well as overall.

The easiest way (I've found) in Node-RED is to set your Inject node to start at different offsets per switch.

illuzn commented 2 years ago

Mate what an excellent idea.... I have them refreshing the state every 5 seconds staggered by 0.4s and CPU usage is down to under 5%