-
Notifications
You must be signed in to change notification settings - Fork 9
Conversation
aa5aa4e
to
6cb16ba
Compare
Trying to make use of the
which seems... inaccurate. I'll remove them for the moment. |
6cb16ba
to
6fc4b88
Compare
OK, removing The value
I want to add tests and see if I can't get it to actually read the current state though, it's annoying that it doesn't, because the format to use in the field is then non-obvious. I'll see what I can do on that before taking it off draft. |
self.mqtt_config.homeassistant().prefix(), | ||
kind, | ||
self.inverter.datalog(), | ||
name.replace('/', "_"), |
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.
Without this, the message for ac_charge/1
ends up nested a level deeper than HA expects. It's not obvious so I decided to centralise it.
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.
Probably just deserves a code comment but yeah seems fine.
(or better, a unit test with a supporting comment)
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.
Test is a very good idea 👍 - will add before I take it off draft.
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.
I had a go at the test and struggled a bit 😬. It can't go in in tests/test_home_assistant.rs
because it's a private method, and I didn't like the idea of pulling that site's common code for setup into src/home_assistant.rs
.
So, I bailed out and just added the comment, plus another of the HA discovery message tests that implicitly exercises this code 😅
@@ -60,6 +63,20 @@ pub struct Number { | |||
unit_of_measurement: String, | |||
} | |||
|
|||
// https://www.home-assistant.io/integrations/text.mqtt/ | |||
#[derive(Debug, Serialize)] | |||
pub struct Text { |
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.
https://github.com/home-assistant/core/blob/master/homeassistant/components/mqtt/discovery.py#L46 shows us all the available controls; just to confirm that there isn't a time, much less a "time range with minute resolution".
@celsworth I wonder what you think of the approach to getting holding register data into home assistant that I've taken here in 64019634d205fb4a86215d6843cf0e8514e27b98 ? Obviously, the The idea is that we publish the mqtt messages mentioned in the HA (I still need to get display of the time fields working, but the others would previously say "unknown" too). I also wonder if, since holding registers have configuration data, these could also be made into retained topics? Since HA stores state in its database, we probably don't need this for the common case (HA restarts after lxp-bridge has run in its presence at least once), but for the initial setup case (lxp-bridge starts, then HA starts for the first time) it'd make a difference. |
I think this approach is fine and it's something that has been considered before so would be happy to have this go in. Obviously though I think it should be a PR of its own and also should be behind a configuration switch. Probably best to default it to off for now? The sleep is a result of you hitting the limitations behind my task control :) Basically channels are setup between all the tasks. Remember all this code is single threaded, but async. As each task gets its turn on the CPU and runs, it subscribes to a channel to listen for "things to do" essentially. If you publish to the other end of that channel before the task has subscribed, you get this "channel closed" error. Until now everything works because nothing publishes before all the tasks are subscribed, but now you've added a publish that could run at any point, even the first thing (not sure if its deterministic) before the MQTT sender has subscribed. The fix is probably to make this all a bit smarter, unfortunately that also means making it a fair bit messier, probably via yet more channels that are essentially used for signalling that a particular task is ready for messages. Then your new method here could wait to receive that message before doing its own publishes. If we start a new PR with this change in, that would be the place to experiment with that probably. I'd be happy to have a go helping with it.
Retaining the holding topics has also been mentioned actually. I do quite like the idea, as in ideal circumstances, the only time most of them should change is when written to (there are exceptions, the current time for instance) and usually we see the new value that has been written when the inverter replies. I say usually because again thats not always true; a If we did retain the topics here we should probably read all of them at startup to have some confidence they're correct, and maybe even read registers that we see have been written to with |
Thinking about this further, even this might not be a great fix because we'd probably run into the opposite problem; your new method might not have subscribed to the signal channel before the MQTT sender tries to tell it its ready! This is awkward :) Maybe we need a shared state object that is basically a key/value store to say when various components of the app are ready. The getter would probably just end up being an async poller with a sleep; just a glorified more responsive version of the sleep you've already got really. Not sure if its worth it.. Edit: having a play with the above idea using |
Something like #144 (WIP) might do the job to avoid the janky sleep. Haven't tested yet and I don't think there's anywhere else its actually needed yet. I'll finish it up later, fix tests and pass it into all the subsystems so at least its ready for use. |
Actually, maybe Sender#receiver_count is a simpler solution. We could just wait for it to go above zero with a short wait and timeout system before aborting. No need for an extra impl/struct at all then.. |
Thanks for looking! Hmm, yeah, |
Hmm. Actually, it's toosimple - we can wait patiently for the Looking at the code a bit more, the inverters don't subscribe to Similarly, even if inverter and coordinator subscribe as expected, the MQTT server doesn't subscribe until after it's made its TCP connection, so we could get the same runtime error there as well - and indeed, if the MQTT server is slow, there's nothing in the existing code stopping us from getting a broadcast from the inverter before that happens, leading to the same runtime error. I'll have a play and see what I can come up with. |
6401963
to
19783ba
Compare
On the topic of configuration - I did wonder if it might make sense to use the If that's not the way to go, I was thinking simple yaml like: mqtt:
homeassistant: {...} # not in this block because it's not actually HA-specific
# ...
publish_holding_registers_on_startup: true WDYT? I also had the thought that maybe it should be implemented per-inverter instead: inverters:
- enabled: true
# ...
heartbeats: true
publish_holding_registers_on_startup: true ? Not sure offhand how that would look with the scheduler approach, but I'm sure it's possible. |
As things stand, I do favour If the scheduler problems can be sorted that could be pretty novel, but I wonder if its getting a bit complex for what 99% of people will need this to do. I'm in two minds about removing the scheduler, the timezone issues are pretty annoying. That said, I do have the timesync running on my inverter and it works well most of the time, just breaks and crashes lxp-bridge in DST transition times :( |
19783ba
to
043aa96
Compare
043aa96
to
90ebdc4
Compare
90ebdc4
to
83b9fb9
Compare
@@ -134,6 +134,22 @@ impl SetTimeRegister { | |||
self.set_register(self.action.register()? + 1, &self.values[2..4]) | |||
.await?; | |||
|
|||
// FIXME: If we only update one of the two registers, we should probably |
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 change publishes a lxp/XX/ac_charge/1 {"start":"00:00","end":"23:59"}
message for us when the command is run. Without it, Home Assistant assumes the attempt was a failure and resets the text field back to the old value after ?some time? ?a restart of lxp-bridge?
The changes to the underlying register are already published, but of course, we don't use them in state_topic
.
I had a couple of little bugs with topic names, but the I don't think we can avoid a dependency on a single MQTT message for both registers here, or at least not without being a lot cleverer than I am with HA. |
83b9fb9
to
464044b
Compare
The underlying "holding register changed" messages are correctly sent with the existing code, but the lxp/X/ac_charge/1, etc, messages are missed. It's important to send these too if we want to rely on the values for Home Assistant state topics.
464044b
to
41a7d7f
Compare
41a7d7f
to
bea5776
Compare
Nice, this looks good. I think we're reaching a natural breaking point to make a release, so unless any showstoppers crop up soon then I'll get 0.10 out as the changelog is getting quite big ;) |
This PR links up the existing ac_change / charge_priority / forced_discharge timeslot configuration topics with homeassistant autodiscovery. To do this, it uses the HA "text" type: https://www.home-assistant.io/integrations/text.mqtt/
The text type is supported in recent homeassistant versions - I'm running the
2023.2
version to test, but a full list is available from this commit: home-assistant/core@2785b2bHere's a screenshot of it in action:
Relies on changes in #147 - that should be merged first. The commit is here too, so the branch works as a whole.
Closes #134