paolosabatino / saj-mqtt-ha

Home Assistant integrator for SAJ H1 inverters
Apache License 2.0
6 stars 5 forks source link

Info requests #6

Open h3llrais3r opened 8 months ago

h3llrais3r commented 8 months ago

Hi Paolo.

I'm going through the code and I'm wondering why this is done: image

Is this really necessary? Can't we just request all 256 registers at once, like we do with the standalone scripts? python readregs.py <mqtt_broker_ip> <inverter_serial> 0x4000 0x100 | python parsedata.py -p

Next to that, I also see in the logs that during initial setup, the query expires after 10 seconds, resulting in an error in the logs. All the next queries are working... Any idea why the initial query (during setup of coordinatior) is always failing? If you enable debug, you'll see that the messages are published, but the subscription does not seem to process then within the 10s timeout. All next published mqtt packages are directly picked up by the subscriber...

image

Greetz, h3llrais3r

paolosabatino commented 8 months ago

Hi Paolo.

Is this really necessary? Can't we just request all 256 registers at once, like we do with the standalone scripts? python readregs.py <mqtt_broker_ip> <inverter_serial> 0x4000 0x100 | python parsedata.py -p

Yes, it is necessary, because each MQTT packet encapsulates a modbus request and the size, in bytes, of the modbus answer can't exceed 256 bytes (123 integer register amount to 246 bytes, plus some overhead)

The standalone script works because the library transparently splits and reassembles the request in multiple MQTT messages exactly as it is happening here.

Next to that, I also see in the logs that during initial setup, the query expires after 10 seconds, resulting in an error in the logs. All the next queries are working... Any idea why the initial query (during setup of coordinatior) is always failing? If you enable debug, you'll see that the messages are published, but the subscription does not seem to process then within the 10s timeout. All next published mqtt packages are directly picked up by the subscriber...

Don't know actually, may be due to the broker not yet ready, or an async method called too early...

h3llrais3r commented 8 months ago

Yes, it is necessary, because each MQTT packet encapsulates a modbus request and the size, in bytes, of the modbus answer can't exceed 256 bytes (123 integer register amount to 246 bytes, plus some overhead)

The standalone script works because the library transparently splits and reassembles the request in multiple MQTT messages exactly as it is happening here

Thanks, that makes sense.

Don't know actually, may be due to the broker not yet ready, or an async method called too early...

That's why I added "mqtt" as depedency in the manifest.json. Now it only loads the integration after that mqtt is loaded. But still getting the timeout for the first query... All next ones are working as expected, but I always see the startup error...

h3llrais3r commented 8 months ago

Could you check your logs after a hass restart to see if you also find the warning that the sensor is taking over 10 seconds?

paolosabatino commented 8 months ago

Apparently yes, I have the same startup error:

dic 12 17:38:28 homeassistant-server hass[6373]: 2023-12-12 17:38:28.522 WARNING (MainThread) [homeassistant.components.sensor] Setup of sensor platform saj_mqtt is taking over 10 seconds.
dic 12 17:38:29 homeassistant-server hass[6373]: 2023-12-12 17:38:29.227 WARNING (MainThread) [saj_mqtt] timeout error, inverter did not answer in expected timeout

and this is in a condition where the MQTT broker is externally configured (so it is not started by HA, but already started) and in this case I just restarted home assistant service without rebooting the machine.

h3llrais3r commented 8 months ago

So it doesn't matter if you run mqtt standalone or as hass addon. The issue is always happening after restart of home assistant (I'm also testing without rebooting the device). So it always happens the first time (it timeouts after 10s, but messages are published -> you can see that if you enable debug logging). So I think it's the subscription on the _rsp topic that doesn't see the messages yet, until the next try. So I'm checking if I can setup the subscription outside of the sensor... maybe that will help.

h3llrais3r commented 7 months ago

Hi Paolo, would like some more information about this: image

Where did you find the info on how to convert a modbus into mqtt? I also would like to know what the fixed values 0x58 and 0xc9 mean. Would be great if you knew it and could share it (and maybe also the documentation where you found it). 😉

paolosabatino commented 7 months ago

Where did you find the info on how to convert a modbus into mqtt? I also would like to know what the fixed values 0x58 and 0xc9 mean. Would be great if you knew it and could share it (and maybe also the documentation where you found it). 😉

Hello! The whole thing is reverse engineered inspecting packets sent by the saj app to the inverter. There is no documentation about the modbus <-> MQTT protocol SAJ uses anywhere.

This is a typical data_transmission MQTT packet, and you can see those two constant bytes there: immagine

Apparently they are just constants, I did not found yet any meaning for them.

h3llrais3r commented 7 months ago

Thanks for the info. Next step would be trying to write to inverter as well (f.e. to change the app mode or the battery charging times) Did you ever try to use your write script as well? Are they working?

PS: Which tool do you use to inspect the mqtt packet? I'm using mqtt explorer, but it's only showing strange characters...

paolosabatino commented 7 months ago

Next step would be trying to write to inverter as well (f.e. to change the app mode or the battery charging times) Did you ever try to use your write script as well? Are they working?

Everyday, never had a problem with the write script. I also have fix-battery-drain.py and fix-osc.py scripts (see the saj-mqtt project) always running to fix some weird behaviour of the inverter and they write registers when they detect anomalies.

fix-battery-drain also changes app mode to "passive" when the battery reach the minimal charge (20%) and restore "self use" when there is PV power, everything works ok. This is done because the inverter consumes more power than the whole house apps standby, and that halves the power consumption of the inverter.

h3llrais3r commented 7 months ago

Great, will investigate a bit more those other scripts you have created. I'm trying to find a solution to charge my battery during the night (on lower cost) as there isn't a lot of sun these days... Probably I have to change the app mode as well for that... but not sure which one...

I found these modes in some documentation of the H1 inverter:

Self-use Mode: When the solar is sufficient, electricity generated by photovoltaic system will be supplied to load first, the surplus energy will be stored in battery, then the excess electricity will be exported to the grid. When the solar is insufficient, the battery will release electricity to supply load.

Time-Div Mode: Battery charging period and discharging period can be set, during charging period, battery can only be charged, while in discharging period, battery can only be discharged, the rest of the period, battery will behave as Self-use mode.

Backup Mode: Battery SOC setting value can be adjusted, when SOC is less than SOC setting value, battery can only be charged, until SOC is larger than SOC_H, the battery will be stopped charging; when SOC is larger than SOC setting value, battery will behave as Self-use mode.

Passive Mode: System will dispatch energy according to user instruction, battery will passively charge from or discharge to grid at certain power to protect battery from being unused over time.

paolosabatino commented 7 months ago

Time-Div Mode: Battery charging period and discharging period can be set, during charging period, battery can only be charged, while in discharging period, battery can only be discharged, the rest of the period, battery will behave as Self-use mode.

this mode probably behaves as you desire. Note that the manual says "can only be discharged/charged", but when I tinkered with the inverter the behaviour was much more like "will be discharged" and "will be charged". It means that if you set a charge period (like during night), the inverter will take energy from the grid to charge the battery at the maximum rate it can; conversely, when you se a discharge period, the battery will be depleted to grid as fast as possible.

There should be some parameters that can be changed for charge and discharge rate, but I could not find them from the front panel of the inverter and probably they can be changed only with the maintenance apps or via modbus.

h3llrais3r commented 7 months ago

The passive mode that you are using in the battery drain, is that doing what I think it does? Does it turns the inverter into "passive" mode and no charging/discharging is happening at all?

h3llrais3r commented 7 months ago

There should be some parameters that can be changed for charge and discharge rate, but I could not find them from the front panel of the inverter and probably they can be changed only with the maintenance apps or via modbus.

Aren't this those parameters? image

I'm still confused about all the modes (except the self-use).

Time-dev seems to let you decide for yourself when to charge and discharge based on time frames Backup seems to only allow charging when between SOC_L and SOC_H Passive (called customized in my firmware) it not really clear to me at all... They talk about "passive" charge from the grid or "passive" discharge to the grid... Do you know what they mean with "passive"? And how to control if it's charging from grid or discharging to grid? Maybe the parameters I listed above?

If I read the above, maybe I could use the backup mode instead? When put in backup mode, the battery would be charged (from grid I suppose as there is no PV production during the night) until it reaches SOC_H?

I'm really anxious for writing parameters, because I don't want to brick my setup...

paolosabatino commented 7 months ago

Never tried backup mode, so I don't really know how it works.

I tried very briefly Time-dev with the outcome I told you in the previous post; after that I well understood what the manual says: it is used to export energy from battery to grid when it is paid more by your electrical company and/or recharge the batteries when the electricity costs less.

Passive mode is just passive: the manual says that it is used when you want to "park" the batteries and perhaps you need to slowly charge or discharge them to avoid they get worn. In my case they don't get charged nor discharged when in passive mode; looking at the inverter temperature and inverter power consumption, I bet the whole battery circuitry is shat down when passive mode is engaged and turned on when self-use is engaged:

immagine

During these days I'm having very little PV production due to bad weather, so battery is almost always drained. As you can see from the tempreature graph, during the night I'm in passive mode, but at 7:20 PV starts producing and self-use is turned on. There you see the nice raising curve of the heatsink temperature. At 17:20 PV stops producing and passive mode is engaged, and there temperature goes down.

The self-use vs. passive mode switching is done automatically by fix-battery-drain.py script from the other project, which also leverage the register 0x3635 Prevent Reverse Flow to avoid excessive battery drain during night: in my case, when in self-use mode, I noticed that during the night the inverter was taking ~100 watts from battery and giving to grid, which is awful because it drains the batteries for about 2 kilowatts during the night, which is 20% of the capacity I have!

The script engages passive mode to avoid the inverter to consume way too much grid energy during night when battery is drained: it's useless to keep the circuitry on when the battery cannot discharge and cannot charge!

Prevent reverse flow instead is used to avoid exporting energy from battery to grid during the night and drain the batteries uselessy.

After all of this, my opinion on SAJ and their inverters is not very good. I don't know how are others, but these corrections I have to do and the other bugs (fixed by the other fix-osc.py script) let me think they don't exactly know what is going on in their products and there is little to no attention to details.

maegibbons commented 4 months ago

Hi Guys

So, I have got this all setup now.

The inverter is actually in Spain. I have poisoned the local dns to redirect the mqtt packets over a vpn to an HA server in the UK.

The broker bridge is configured for both directions and since dns is seperate on the HA network the address in the bridge configuration can be left as dns name rather than fixed ip.

I added the data_transmission topics to the bridge config as well as I wanted the app to continue working.

All working well.

Have any of you got any example HA automations or scripts for setting overnight charging? Particularly changing the charging power.

Great integration BTW and I look forward to contributing when up to speed.

I maintain an integration for LUXpower inverters also based on modbus but encapsulated in a multi-inverter wrapper akin to but not the same as topics in mqtt.

Krs

Mark

h3llrais3r commented 4 months ago

Have any of you got any example HA automations or scripts for setting overnight charging? Particularly changing the charging power.

Hi, no HA automations for the moment from my side. You can always try to have a look at python scripts in the other repo of Paolo (saj-mqtt) as he has some automations running, but outside HA. I will probably only start on HA automations at the end of the year, when overnight charging because useful again, as as of now, it's not needed anymore from my side, as there is enough sunlight during the day. The only thing I did, up until now for overnight charging, was setting up the 'time of use' window to only charge at a specific speed (f.e. 1kwh) during a specific time (f.e. between 00:00 and 05:00). With a manual toggle I can then switch between the app modes to either use 'self use' mode or 'time of use' mode.

maegibbons commented 4 months ago

Did you do that with a switch entity in ha translated to a register write?

or did you set it up in the app?

if in ha, have you got example code for that?

Krs

Mark

h3llrais3r commented 4 months ago

I've added a service to set an app mode. I've added also services to the HA integration to read and write registers. See https://github.com/paolosabatino/saj-mqtt-ha?tab=readme-ov-file#ha-services Be aware that using the write_register service is AT YOUR OWN RISK, as you have to specify yourself which value you are writing to which register. If you write wrong values to the registers, your inverter might be damaged indefinitely. The set_app_mode service is actually using the write_register service internally to write the APP_MODE value to the APP_MODE register. So before writing stuff to your register, PLEASE MAKE SURE you are writing a valid value to the correct register. Since there are a few hardware/software versions out there, it might be that the register for 1 version is not containing the same data as for another version. So I suggest that you first read the value of a register, to see if it's the correct one, and then write a new value to it. What I typically do is changing the value first via the O&M app and then read it to make sure it's the right one.

Like I said above, until now, I've only used the set_app_mode service, to write a new app mode to the register (which works). I've set up the configuration for the TIME_OF_USE mode manually via the O&M app, so I didn't change that via the integration. For me this was sufficient, as I only need a dedicated timeslot where I want to charge my battery from the grid during the TIME_OF_USE mode. Outside this dedicated timeslot to charge from the grid, the inverter is working as in SELF_USE mode.

maegibbons commented 3 months ago

Hi

Has anybody tried forced exporting using either the integration or the esolar app "remote" section?

I can get it to charge using time of use settings but I cannot get it to force discharge from batteries using TOU.

Set up exactly the same as charge in the app.

It is just ignored.

Can anybody else try it to see if it works for them?

Krs

Mark