FruitieX / homectl-server

A simple home automation server written in Rust
MIT License
80 stars 10 forks source link

Partial state updates (rust newbie question) #6

Closed evelant closed 2 years ago

evelant commented 2 years ago

Sorry if there's an obvious answer to this, I'm just learning rust. For my first "real" rust project I'm writing a Hubitat maker api integration for homectl.

I've partially hacked it together but I'm uncertain about the best way to handle device state updates from Hubitat. Hubitat sends only the part of the state that has changed via a POST to homectl. From reading the code it seems that homectl is designed to require a full Device struct with the entire device state in the IntegrationDeviceRefresh message. I don't see a way to construct a valid Device given the state change POST from Hubitat only contains the device id and the bit of changed state.

Would it make sense to refactor IntegrationDeviceRefresh to take partial state updates for a device? Or would it make sense to somehow expose all device state to the integration and construct a full IntegrationDeviceRefresh there by merging the current state with the incoming state change?

Again sorry if this is a really basic or obvious question, there's a lot to learn with rust! Thanks for any insight, this is a really cool project!

FruitieX commented 2 years ago

Sorry for the late answer, I've completely missed this issue! Need to make sure I get GitHub notifications in my e-mail inbox (and that I read them :-)). I think you're among the first users to try this project out.

Yeah it's a bit annoying that you have to provide the full Device struct with every update. It'd probably make sense to eventually refactor IntegrationDeviceRefresh to contain some form of state update message rather than the full Device as you say.

In general I'm thinking about refactoring how the Integration trait works, or at least building a layer on top of it to handle polling for you. Right now every integration that needs polling has to handle all polling logic themselves, but the more integrations I add the more often I have to do this same thing over and over which is getting tedious. I'm sure there's a lot more architectural changes that we can make to homectl that would improve things. After all it is my first Rust project as well.

For now I would suggest that you keep track of the device's state in your integration's struct. During the register phase you can request initial state of the device and store it, and when you get a diff from a POST response, you should be able to make changes to this state and rebuild a Device from the new state. A recent integration in backend/src/integrations/tuya/mod.rs has a similar approach with its device_expected_state field. Although this is used for sending expected state to the device every poll interval, not for sending updates toward homectl core. Note that the state is wrapped in an Arc<RwLock<_>> to make sure only one thread can access (and mutate) the data at once. If you were to forget this, you'd get a compilation error.

Happy to assist further, and if you need any more help, there's now a Discord link in the README. If you get the integration working contributions are welcome too!

FruitieX commented 2 years ago

I added some documentation on the configuration format which might be helpful to get started:

https://github.com/FruitieX/homectl#sample-configs-for-supported-integrations https://github.com/FruitieX/homectl#configuration-tips

FruitieX commented 2 years ago

Let me know if you need any further help with this issue.