diy solar

diy solar

JK-BMS + Grafana in one evening.

Hmm. Unfortunately not. I fell out with the JK-BMS's balancer for being rubbish and installed a Heltec balancer, which has a "switch" contact you can put a relay or smart switch (dry contact) onto.

I haven't implemented that yet either, awaiting a battery upgrade, which has help the miss matching of cells a bunch and made the balancing less of an issue.

All I can propose is googling around the problem, I recall seeing people working on the "command" set for RS485 but its binary and complex and poorly documented.

The ESPHome component code should reveal the command binary sent over the BT packet to "query all registers". In theory if you can find reference to the command binary for "Balancer ON", you could probably hack it in, or ask the developer for support.

If you get anywhere near that, post back here and I'll try and help. If I spot something on my travels I'll do the same.
 
Hmm. Unfortunately not. I fell out with the JK-BMS's balancer for being rubbish and installed a Heltec balancer, which has a "switch" contact you can put a relay or smart switch (dry contact) onto.

I haven't implemented that yet either, awaiting a battery upgrade, which has help the miss matching of cells a bunch and made the balancing less of an issue.

All I can propose is googling around the problem, I recall seeing people working on the "command" set for RS485 but its binary and complex and poorly documented.

The ESPHome component code should reveal the command binary sent over the BT packet to "query all registers". In theory if you can find reference to the command binary for "Balancer ON", you could probably hack it in, or ask the developer for support.

If you get anywhere near that, post back here and I'll try and help. If I spot something on my travels I'll do the same.
Alright, it will be a pleasure to share my findings.
Thank you for taking the time to answer, much appreciated
 
So it turns out to be as simple as publishing "ON" or "OFF" to the <BMS>/switch/<switch>/state topics.

1690368502996.png


Or from HA with Lovelace
 
Thank you Venquessa,
I gave a look to the links you posted.
I am actually looking for the Hex frames to send to the bms through the UART-TTL connected to the bms GPS port and neither of the links (to my understanding) gives a clue about what is going on behind the scenes.
Moreover, one of them it seems to be using BLE to operate the switch and I am bound to use UART-TTL.
 
Hmm. Unfortunately not. I fell out with the JK-BMS's balancer for being rubbish and installed a Heltec balancer, which has a "switch" contact you can put a relay or smart switch (dry contact) onto.

I haven't implemented that yet either, awaiting a battery upgrade, which has help the miss matching of cells a bunch and made the balancing less of an issue.

All I can propose is googling around the problem, I recall seeing people working on the "command" set for RS485 but its binary and complex and poorly documented.

The ESPHome component code should reveal the command binary sent over the BT packet to "query all registers". In theory if you can find reference to the command binary for "Balancer ON", you could probably hack it in, or ask the developer for support.

If you get anywhere near that, post back here and I'll try and help. If I spot something on my travels I'll do the same.
Surprised to read that you have issues with the build in balancer. What kind of issues are you experiencing?
 
Thank you Venquessa,
I gave a look to the links you posted.
I am actually looking for the Hex frames to send to the bms through the UART-TTL connected to the bms GPS port and neither of the links (to my understanding) gives a clue about what is going on behind the scenes.
Moreover, one of them it seems to be using BLE to operate the switch and I am bound to use UART-TTL.

I believe the BLE protocol is just basic serial coms on top. The UART code seems identical to the BLE.


This includes the registers for the "switches".

The command code for write register is
static const uint8_t FUNCTION_WRITE_REGISTER = 0x02;

here:

It "looks like":
0x02 0x9D

Would toggle the balancer.
 
Surprised to read that you have issues with the build in balancer. What kind of issues are you experiencing?

Fixation and low power. Mine is the 0.6Amp balancer model, which isn't enough in many cases. It also fixates on a pair of cells and will only balance that pair until it's done. Even if there is another cell approaching or even hitting LVC.
 
The command code for write register is
static const uint8_t FUNCTION_WRITE_REGISTER = 0x02;

It "looks like":
0x02 0x9D

Would toggle the balancer.
True, but sending the RS485 frame using those values, even if the answer from the bms seems fine, actually doesn't toggle the balancer switch.
I am using the same frame format to toggle the charging and discharging switches and it works fine, so I was expecting the same for the balancer but nope.
 
Very Impressive Work @venquessa ! But I don't think you managed everything in one evening 😁.

I was scrolling through your GitLab Repo to try to understand ... You really put A LOT of work into it (y).

Any tips on how to get started ? I just recently started playing with Podman (similar to Docker) Containers. Python I did something a few years back but only recently started again.

I was trying some stuff with Python as well for Solar Power Balance Controller in order to optimize Battery Usage / lower Energy Cost / Maximize Solar Energy Production etc. Using paho.mqtt library in Python seems less intuitive that I originally thought ...

Home Assistant and MQTT-Explorer on the other hand ... Spin a Docker container and debug is as easy as 1-2-3.
 
Sorry for late reply.

Absolutely there was "prior work" in my 'core systems' which helped me alone the way.

You are not far off from what I have, by the sounds of it.

On Paho. If you look into my code base for the mqtt-lib you will see how I wrap it in something a bit more "fit for purpose".

The tricky part to wrap your head around with MQTT in general is that it leans heavily towards the "asynchronous event model". This presents some interesting things which, if not aware of, will cause horrendous amounts of confusion and concurrency issues with state.

The idiom of "If this then that" is rather easy to envision and implement with an "on_message" handler. It is when you come to "If this and another thing, for at least 2 minutes, then that" or "two minutes after this do that, unless this happens" it starts to bend your head and brings those "interesting things" out to play. In the event model, events may arrive effectively concurrently. Depending on some settings they may even arrive out of order. Messages get missed, messages get resent, dropped, queued, delayed, messages get repeated over and over. Any attempt to keep state in a sane and consistent fashion, especially when handling things like service restarts or full reboots... involves serious engineering.

So I treat state like a hot potato. Assume you don't know it in advance and do something sensible.

Consider this simple(?) scenario:

You want to increase your solar charge limiter, if and only if the battery balancer is idle. You can handle both messages from the solar charger and from the balancer. The trouble is, no single message context contains both data.

Why not ask the balancer? Assume you don't know it's current state, so ask it via it's MQTT get API. This introduces one of the hardest things to do right on an async message bus. The simple Request/Response flow is actually non-trivial to implement.

The trick I used, was to simply cache the last message on all subscribed topics. The only event my automations actually receive is a notification that the cache has changed for a topic. It is then completely up to them if they are even interested in responding to that topic and if they are how to respond.

My lights for example subscribe to both the motion sensors, but also the MPPT output watts for the solar. It does not respond to any changes in the later, but when motion is detected it will check the "last on topic" for the solar panel power and decide if the lights are required or not.

"If this for 2 minutes, then that" I solve by flipping them around.

"Unless told otherwise do this for or in 2 minutes"

Say a temperature change ends up causing the radiator to need switching on. I put a "timeout" on the request for the radiator of 3 minutes. If nothing resets that "timer" the automatics will turn the radiator off. Doing it for the inverse is just as easy, but harder to explain. Basically it's about publishing "future intentions", like publishing "calender reminder items" with specific dates and actions which something will watch over.

On the MQTT abstraction the code is here:
Specifically the MqttMultiTopicObservableCache class
If you have a scenario where you MUST process each and every message in turn, then the appropriate abstraction is the MqttTopicQueue class

On implementing services using this, look at something very basic like the "MPPT Totaler" service here:

In general I recommend taking the "Micro-service" concept seriously. Keep your python services small. If you have a more complicated process to handle, it is often better to split it into several smaller services responding to events from each other. This can also be seen in the above, where the MPPT total isn't available on the bus, so I wrote a service to calculate it. Then a second service to respond to that and the battery BMS and a third to control the actual panel limiters.

Note. Doing this, adding a python micro-service layer, does not prevent your HA setup from working as is, and can even intergrate between your own services and HA easily via the MQTT intergrations.
 
Sorry for late reply.

Absolutely there was "prior work" in my 'core systems' which helped me alone the way.

You are not far off from what I have, by the sounds of it.

On Paho. If you look into my code base for the mqtt-lib you will see how I wrap it in something a bit more "fit for purpose".

The tricky part to wrap your head around with MQTT in general is that it leans heavily towards the "asynchronous event model". This presents some interesting things which, if not aware of, will cause horrendous amounts of confusion and concurrency issues with state.

The idiom of "If this then that" is rather easy to envision and implement with an "on_message" handler. It is when you come to "If this and another thing, for at least 2 minutes, then that" or "two minutes after this do that, unless this happens" it starts to bend your head and brings those "interesting things" out to play. In the event model, events may arrive effectively concurrently. Depending on some settings they may even arrive out of order. Messages get missed, messages get resent, dropped, queued, delayed, messages get repeated over and over. Any attempt to keep state in a sane and consistent fashion, especially when handling things like service restarts or full reboots... involves serious engineering.

So I treat state like a hot potato. Assume you don't know it in advance and do something sensible.

Consider this simple(?) scenario:

You want to increase your solar charge limiter, if and only if the battery balancer is idle. You can handle both messages from the solar charger and from the balancer. The trouble is, no single message context contains both data.

Why not ask the balancer? Assume you don't know it's current state, so ask it via it's MQTT get API. This introduces one of the hardest things to do right on an async message bus. The simple Request/Response flow is actually non-trivial to implement.

The trick I used, was to simply cache the last message on all subscribed topics. The only event my automations actually receive is a notification that the cache has changed for a topic. It is then completely up to them if they are even interested in responding to that topic and if they are how to respond.

My lights for example subscribe to both the motion sensors, but also the MPPT output watts for the solar. It does not respond to any changes in the later, but when motion is detected it will check the "last on topic" for the solar panel power and decide if the lights are required or not.

"If this for 2 minutes, then that" I solve by flipping them around.

"Unless told otherwise do this for or in 2 minutes"

Say a temperature change ends up causing the radiator to need switching on. I put a "timeout" on the request for the radiator of 3 minutes. If nothing resets that "timer" the automatics will turn the radiator off. Doing it for the inverse is just as easy, but harder to explain. Basically it's about publishing "future intentions", like publishing "calender reminder items" with specific dates and actions which something will watch over.

On the MQTT abstraction the code is here:
Specifically the MqttMultiTopicObservableCache class
If you have a scenario where you MUST process each and every message in turn, then the appropriate abstraction is the MqttTopicQueue class

On implementing services using this, look at something very basic like the "MPPT Totaler" service here:

In general I recommend taking the "Micro-service" concept seriously. Keep your python services small. If you have a more complicated process to handle, it is often better to split it into several smaller services responding to events from each other. This can also be seen in the above, where the MPPT total isn't available on the bus, so I wrote a service to calculate it. Then a second service to respond to that and the battery BMS and a third to control the actual panel limiters.

Note. Doing this, adding a python micro-service layer, does not prevent your HA setup from working as is, and can even intergrate between your own services and HA easily via the MQTT intergrations

Thank you for your reply. No worries for the delay. You know, I am jumping between like 10 different projects at the time. Building batter #03 & #04, Setting up the new DC distribution busbars for my batteries & cabling, trying to implement JK BMS Inverter Communication using @Der_Hannes solution from https://diysolarforum.com/threads/j...g-logic-open-source.79325/page-6#post-1029595, trying to get Netbird working to access Home Assistant remotely since Tailscale and Zerotier seem to not work very well on Android (but then the identity provider for Netbird - Authentik at the moment - doesn't work at the moment with Podman on a VPS Server). Looots of stuff :ROFLMAO:

I am/was working on this:

But maybe I'll rename it solar-mqtt2database since I am currently debating whether to use InfluxDB or TimescaleDB (PostgreSQL for TimeSeries basically) or both ...
But yeah, the idea is quite simply: specify in a YAML file WHAT to look out for (topics), then WHERE to put it (database) etc.

One thing that I see is quite tricky to get it working is to shut down Podman gracefully when your script/app does an infitite loop. The solution I have at the moment, using the class GracefulKiller from Stackoverflow, doesn't really work IMHO. Maybe it's Podman and non-Docker related, but still, something feels off.

Another option is to use AsyncIO or the likes of it, but I still need a main infinite loop externally. Or do I :unsure: ? I coudn't get asyncio to work properly with CAN communication in:

But then again, jumping from project to project for sure does not help ...
 
Regarding while(true) loops versus event handlers. I think you are describing what I call the "dynamics" or "cadence drivers".

Basically, what invokes the code to do something?

As you (?) are exploring MQTT the natural answer is... the message receive event for a topic. The "on_message" callback handlers. In this scenario you have two options. Paho client in "blocking_run" where it runs in the 'main' thread. Or, non blocking run, where it spawns a background thread and your python script is returned to execute.

This is canonically called the "push" mechanism. As the wider system pushes things towards the services and the services respond on demand only.

The other option and sometimes necessary, is to "pull". Typically this is where those while(true) loops come into play.

while(true) {
checkSomeState
if someState:
do stuff.
sleep someTime
}

This is an age old "polling" or "pull" loop.

There should be no problem shutting down a pod/container running such a loop. What you may experience however is zombie threads. When you have more than one thread active in most programming languages the child threads do not automatically respond to the parents signals. The system tells the parent to exit, it is up to that parent to tell the kids. If you (or your framework) do not implement this it can result in the python interrupter not exiting when you send an "SIG_INT" or a "SIG_TERM". Your main script exits, but the child threads hold the lock and keep the process running. Usually a further INT or, if you have to a KILL (which sounds like where you went) will fix the problem.

If you have state which must be closed, such as in transactional systems, you would be best implmenting the signal handler and sorting out the child threads yourself. If you have no state to corrupt, the "kill" is fine.

Using the Paho MQTT "blocking run" I find that docker can stop a container in about 5 seconds. However it does send the TERM and wait 5 seconds before sending a KILL.

I use docker-compose on Ubuntu. So, while I have 16 or so services, they are just grouped into 3 docker-compose scripts.

Docker compose just makes docker so much easier to deal with. There is a repo with my docker-compose deployment things in it, but it's not public at this moment. Laziness rather than privacy.
 
Oh. Of uptmost importance with the "push" model and on_message() callbacks. With Paho, they are "in paho thread". It uses one main thread. If you spend 2 minutes in your on_message handler, then 2 minutes of all messages will cue up.

If you can respond immediately you can. If you can't respond immediately, you will need a thread/timer or publish future intents.
 
Also. Those git repos are open source, so feel free to borrow or steal.

I'd prefer you fork it and pass back anything you do that's cool, but I don't mind you even copy and pasting it, just don't take credits and do try and let me know what you use it for, for my own curiosity.
 
Oh. Of uptmost importance with the "push" model and on_message() callbacks. With Paho, they are "in paho thread". It uses one main thread. If you spend 2 minutes in your on_message handler, then 2 minutes of all messages will cue up.

If you can respond immediately you can. If you can't respond immediately, you will need a thread/timer or publish future intents.
Or separate data processing / reply threads using asyncio or multiprocessing etc. That's what I try to do in (in CANbus though) where the intermediary class get (at arbitrary times) user inputs, but must send (at fixed intervals) CAN data to the charger

Granted it doesn't work yet, but I'm just referring to the architecture principle :)
 
Also. Those git repos are open source, so feel free to borrow or steal.

I'd prefer you fork it and pass back anything you do that's cool, but I don't mind you even copy and pasting it, just don't take credits and do try and let me know what you use it for, for my own curiosity.
Well I tend to license everything I do under AGPL (basically GPL v3 plus a clause on "Cloud services" etc which is probably not relevant anyways) which might be different than yours. I don't have a problem integrating parts of your code if it doesn't create a license issue (for you) :). References and back links to your repo would be provided of course :)
 
Or separate data processing / reply threads using asyncio or multiprocessing etc.
Absolutely. Especially focusing on the later. In fact I tend towards only the later. When I went through some architectural design for this, I kept finding "state machines". State machines and concurrency is a recipe which will make me sit up and listen in work as it's either a bad, bad idea or if it HAS to be done that way, a hell of a lot of rock breaking matrix testing work.

Instead the approach I took was to break everything into it's own little process. Instead of my original monolithical "hub" process, I began writing many smaller components and had them share information via a TCP/UDP "buliten board", basically a single topic message bus. Immediately this confines state to the individual processes. The Scheduler process, for example, cannot share or tamper with state of the HTTP Device controller. Immediately removing a massive source of bugs AND if the services are granular enough that they don't need threads or timers, or state at all.... all concurrency issues go away. State is untrusted, mutable, transient and assumed unpresent by default.

When I embraced MQTT it became the obvious solution to replace the "buliten board" which was rapidly expanding to 4k of data being passed around 100s of times a minute.

Each service cares only what it subscribes to on the bus. It doesn't care what other services exist or what they do or if they are offline. It responds to it's topics. A failure scenario includes the temperature sensor not sending enough updates, often enough and causing the heating to cycle on and off. Fixable, but an example of the limitations of "pure push".

This means you can freely add services with little or no concern to managing state of others or respecting what other services are doing even.

I know this is about lights, and I know I'm "going on", but to give you some flavour, the current automation I am trying to do involves more fine grained control of lights based on multiple occupancy sensors.

I "clean roomed" the requirements. No assumptions on implmentation.

For my kitchen lights to be automated, control free, I need some special considerations and behaviour. I would like them to come on initially based on "motion", for 30 seconds only. "Transient pre-start mode". If after 30 seconds motion has not been detected again, the lights can go out immediately. If however motion has continued for those 30 seconds, the lights should now "latch on for 2 minute windows of motion". This reduces the amount of accidental switch offs, which are particularly dangerous in a kitchen.

But how do you "wait for 30 seconds" in a stateless service? You could just call sleep(30), but that would lock up that particular service, which may, or may not be feasible. You could launch a thread or a timer to send the OFF message in 30 seconds and on receiving another motion=true message you could reset that timer to 2 minutes... repeat.

Now what happens if the service is restarted? That timer is forgotten about entirely. Nothing turns the light off. I could have a service dedicated to looking for "unintended state" and fixing it. I have such a mechanism in my heating system. It's messy though. In my opinion the system state should gravitate towards a safe and usually OFF state itself naturally unless given impetus to do different.

So. I intend to do this using the same concept I used for heating. Requests -> demands -> controls. Each has a timeout. Initially the timeout will be 30 seconds. The "controls" will turn the bulb on. When the 30 seconds have passed that control will expire and the light will be switched off. If motion is re-detected at any point and such a "control" exists, it will be extended in time based on logic required.

It needs at least 2 services working in tandem, in my heating system there are about 6, but, each of those 6 are made very, very simple and very focused on what part they play. So each to maintain and modify.

Request: Any random service, device or user may publish a request for heating or lighting in a zone. Expect multiple competing and conflicting requests.
Demand: A single validated instance used to signal some heating or lighting is required. Basically reconciles the requests into a non-competitive form using a basic rules engines on what can and cannot override or overrule other requests.
Control: The message published which causes the device controllers (integations in HA lang) use to control the state of the device. (and publish that state).
 
Last edited:
Well I tend to license everything I do under AGPL (basically GPL v3 plus a clause on "Cloud services" etc which is probably not relevant anyways) which might be different than yours. I don't have a problem integrating parts of your code if it doesn't create a license issue (for you) :). References and back links to your repo would be provided of course :)
I think I intended the MIT license. The original messy repo is MIT I believe.

The new tidier repo is unlicensed I think. I don't recall applying one in gitlab for it. So basically public domain.

It's a "one man band project", just my ideas and efforts over the past 6 years or so. Many of these exist out there. Few get any traction.

If you or anyone is seeking a "one or two sizes fits all", then Home Assistant. The thing I find is... with a little careful architecture you can solve your own bespoke problems for yourself and only yourself in about 1/1000th of the code than trying to solve everyone's problems for all different people and devices in one application. The later is a MASSIVE undertaking. Bespoke, just for my uses is 1000 times simplier. If... if you are willing to learn and get some software skills.
 
Absolutely. Especially focusing on the later. In fact I tend towards only the later. When I went through some architectural design for this, I kept finding "state machines". State machines and concurrency is a recipe which will make me sit up and listen in work as it's either a bad, bad idea or if it HAS to be done that way, a hell of a lot of rock breaking matrix testing work.

Instead the approach I took was to break everything into it's own little process. Instead of my original monolithical "hub" process, I began writing many smaller components and had them share information via a TCP/UDP "buliten board", basically a single topic message bus. Immediately this confines state to the individual processes. The Scheduler process, for example, cannot share or tamper with state of the HTTP Device controller. Immediately removing a massive source of bugs AND if the services are granular enough that they don't need threads or timers, or state at all.... all concurrency issues go away. State is untrusted, mutable, transient and assumed unpresent by default.

When I embraced MQTT it became the obvious solution to replace the "buliten board" which was rapidly expanding to 4k of data being passed around 100s of times a minute.

Each service cares only what it subscribes to on the bus. It doesn't care what other services exist or what they do or if they are offline. It responds to it's topics. A failure scenario includes the temperature sensor not sending enough updates, often enough and causing the heating to cycle on and off. Fixable, but an example of the limitations of "pure push".

This means you can freely add services with little or no concern to managing state of others or respecting what other services are doing even.

I know this is about lights, and I know I'm "going on", but to give you some flavour, the current automation I am trying to do involves more fine grained control of lights based on multiple occupancy sensors.

I "clean roomed" the requirements. No assumptions on implmentation.

For my kitchen lights to be automated, control free, I need some special considerations and behaviour. I would like them to come on initially based on "motion", for 30 seconds only. "Transient pre-start mode". If after 30 seconds motion has not been detected again, the lights can go out immediately. If however motion has continued for those 30 seconds, the lights should now "latch on for 2 minute windows of motion". This reduces the amount of accidental switch offs, which are particularly dangerous in a kitchen.

But how do you "wait for 30 seconds" in a stateless service? You could just call sleep(30), but that would lock up that particular service, which may, or may not be feasible. You could launch a thread or a timer to send the OFF message in 30 seconds and on receiving another motion=true message you could reset that timer to 2 minutes... repeat.

Now what happens if the service is restarted? That timer is forgotten about entirely. Nothing turns the light off. I could have a service dedicated to looking for "unintended state" and fixing it. I have such a mechanism in my heating system. It's messy though. In my opinion the system state should gravitate towards a safe and usually OFF state itself naturally unless given impetus to do different.

So. I intend to do this using the same concept I used for heating. Requests -> demands -> controls. Each has a timeout. Initially the timeout will be 30 seconds. The "controls" will turn the bulb on. When the 30 seconds have passed that control will expire and the light will be switched off. If motion is re-detected at any point and such a "control" exists, it will be extended in time based on logic required.

It needs at least 2 services working in tandem, in my heating system there are about 6, but, each of those 6 are made very, very simple and very focused on what part they play. So each to maintain and modify.
My initial idea (instead of your TCP/UDP bulletin board) was to use Flash+REST API to communicate between services. But right now I am still unsure if this is the best approach ...
 
REST API with micro-services is a valid approach. Along with RMI (Remote method invocation).

The advantages of using "middleware" like a message bus (MQTT) is that services do not need to know of each other's existence, nor do they need to "discover" them.

In a traditional, non-message bus micro-service architecture, services are located via DNS or a central "directory".

In a straw man, on receiving the new temperature update, you want to turn the heating on, you would then go to the "directory" look up service and ask "Where is the heating controller for that zone?". You would hopefully get a REST end point URL back.

All work. Swings and roundabouts. However, IMHPO the message bus takes care of so much of the lower level communications that it's hard to resist.

As described above, where it becomes far more challenging is when you need to deal with transactional scopes....especially across multiple services. An HTTP/REST or RMI microservice arch is fully capable of making transactional calls between services. Doing so on a message bus async arch is... more tricky and usually left up to the developer. Frameworks exist, but only to my knowledge for the likes of "Kafka" and "RabbitMQue".
 
Back
Top