-
-
Notifications
You must be signed in to change notification settings - Fork 11
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
Support for resuming Hue event stream #64
Conversation
Very exciting! Thank you for working on this :) I was wondering about this problem too, but I didn't realize that the hue app sends a "last seen"-identifier! I'll take a look at it immediately 👍 |
There's some really good stuff in here - and it's super exciting that you found a way to make the stream resumable :) I also agree with you that I think I see a solution;
It's a rough sketch - but you see what I mean? |
Yep, that makes sense to me. I did a quick test with 1024 events in the buffer and didn't really notice any difference in performance or memory usage. 1024 is probably overkill so maybe we can set it to something like 128? We could also be fancy and only keep the last relevant change per entity, but I'm not sure if that's worth the trouble. Another possible optimization I noticed is to chunk changes. Not sure if that would make any difference, but I noticed that the hue bridge does it for changes to grouped lights. |
1024 events in rust structs would only cost a handful of KB, so no worries there. I think 128 is a good starting point. Let's make the queue size an argument to We shouldn't spend time trying to be overly fancy here, I don't think it's worth it. In the future, we could do something fancier, like adding an internal "last modified" timestamp to all objects, allowing us to synthesize history for clients that connect after the queue limit. Or, as an alternative, we could have bifrost simply synthesize the entire state as big chunky update, if someone connects with an old timestamp. Or maybe just make the request fail, if the timestamp is expired? That might be enough to make the app connect from the top? In any case, all that is overkill for now :) |
The |
Nice improvement! Seems to be working well :) I think we should collapse these: pub fn hue_channel(&self) -> Receiver<(String, EventBlock)> {
self.hue_event_stream.subscribe()
}
pub fn hue_events_sent_after_id(&self, id: &str) -> Vec<(String, EventBlock)> {
self.hue_event_stream.events_sent_after_id(id)
} into pub fn hue_event_stream(&self) -> &HueEventStream {
&self.hue_event_stream
} Then we use this to get a reference to the event stream, and call the functions on there. Also, I'm not a big fan of having the struct HueEventRecord {
timestamp: DateTime<Utc>,
index: u32,
block: HueEventBlock,
} This could then have a function on it to generate the sse timestamps. What do you think? |
Updated PR with suggestions now. Ended up just using |
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 is looking really good!
I just have some nitpicks (which means we're very close to merging 😆)
Oh, and can you please rebase on top of master? It's had some updates since your branch.
Thank you very much for this exciting new feature :)
I think I've addressed all the feedback now (which I appreciate as the code is much better now than what I hacked together yesterday 😄 ) |
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.
Just tested it - it works great!
Thank you very much for your contribution! :)
Before this patch the Hue app goes out of sync with the current Bifrost state if changes are happening while the app is closed. For example by changing brightness in zigbee2mqtt directly. This seems to be because Hue doesn't do a full refresh when opening the app and instead relies on using Last-Event-Id to resume the event stream where it left off.
I've therefore implemented support for storing the last 32 events in a circular buffer (actually a VecDeque) which can be sent when the client connects if it provides an event-id. I did some quick reverse engineering of the Hue bridge and it looks like it just stores the last 15 events so it's very much possible to fill this buffer with other changes and miss an entity update. Since z2m seems to be more chatty I think it makes to increase the number of stored events even more. I doubt it will affect performance and memory usage much, but I haven't looked into it.
I ended up moving event id handling down to
Resources
and instead generate it when first receiving the event. I'm not super happy about puttingprev_ts
andidx
directly inResources
, so I'll gladly take suggestions on how to improve that.