Open NickSutton opened 1 year ago
I was able to make some progress with this, and it's actually fairly straight forward as the developer has already included the code to write a change to the inverter on receipt of a MQTT message.
So, for anyone else wanting to achieve the same these are the steps I put in place:
Subscribing to MQTT Topics Your remote Pi Zero W, or whatever you have attached to the inverter is busy sending information from the solis to Home Assistant via MQTT. Presently it is not listening for replies from HA. To do this we need to get the Pi Zero W (or whatever) to subscribe to a 'topic' so it can listen out for those communications.
This piece of code, already included in solis2mqtt.py
does just that:
def subscribe(self):
for entry in self.register_cfg:
if 'write_function_code' in entry['modbus']:
if not self.mqtt.on_message:
self.mqtt.on_message = self.on_mqtt_message
logging.info("Subscribing to: "+self.cfg['inverter']['name'] + "/" + entry['name'] + "/set")
self.mqtt.persistent_subscribe(self.cfg['inverter']['name'] + "/" + entry['name'] + "/set")
If we break this down we can see that the code is checking each of the registry blocks (in solis_modbus.yaml
) for a write_function_code
entry. If such an entry exists then MQTT will subscribe to a topic named as:
"+self.cfg['inverter']['name'] + "/" + entry['name'] + "/set"
The above naming draws from the config.yaml to get the inverter name, typically solis2mqtt
followed by the name of the register block that contains the write_function_code
entry. So could be something like:
solis2mqtt/write_storage_control/set
Now, in solis_modbus.yaml
add a block in the following format:
- name: write_storage_control
active: true
modbus:
register: 43110
number_of_decimals: 0
write_function_code: 6
input_type: holding
signed: false
It would be handy if we could build a list of the writable register codes here, 43110
is used to change the storage control value (typically 33/35)
Restart the process on the remote Pi Zero W with sudo systemctl restart solis2mqtt
to take in to account the new register block.
You should see in the log that the topic (in this case) solis2mqtt/write_storage_control/set
has been subscribed to, which means the Pi Zero W is now listening.
From HA you can use dev tools > services to issue a MQTT communication in a designated topic:
The above should set your storage control value to 35 (if it is 33 currently etc).
To change different settings on the inverter just add new register blocks (with the correct register value!!) then issue new values to that MQTT topic.
Disclaimer: I don't know what I'm talking about most of the time and figured all this out in the middle of the night but it seems to work, as does my inverter. That said, if you mess this up you might well brick your inverter and need a replacement etc. All of the above at your own risk, I accept no responsibility for anything, at all, ever.
43110
- Storage control, usually 33 / 35
43000
- Inverter date/time: Year
43001
- Inverter date/time: Month
43002
- Inverter date/time: Day
43003
- Inverter date/time: Hour
43004
- Inverter date/time: Minute
43141
is the fixed period charge current in deci-Amps (ie 500 -> 50A) for dynamically changing the charge current limit
43141
- Time Charge Current
43142
- Time Discharge Current
43143
- Time Charge Start Hour
43144
- Time Charge Start Minute
43145
- Time Charge End Hour
43146
- Time Charge End Minute
This link is very useful: https://www.boards.ie/discussion/2058224611/solar-pv-monitoring-automation-thread/p25?fbclid=IwAR2J6yGwUrC6bol7VZ3YeoKDHZnY2H0ybJRIUNlsszz5l51GBee8dowKDng
43110
- is defined bit-wise as follows:
Bit 0 (1) - Spontaneous Mode input_boolean.solis_storage_mode_spontaneous
Bit 1 (2) - Timed Mode input_boolean.solis_storage_mode_timed
Bit 2 (4) - Off-Grid Mode input_boolean.solis_storage_mode_off_grid
Bit 3 (8) - Battery Wake-Up Mode input_boolean.solis_storage_mode_wake_up
Bit 4 (16) - Backup Mode input_boolean.solis_storage_mode_backup
Bit 5 (32) - Grid Charge Mode input_boolean.solis_storage_mode_grid_charge
So for timed charging you need both Bit 5 (32) and Bit 1 (2). Bit 0 seems to tell it to spontaneously follow the mode outside of timed periods (ie Self-Use). Hence 35 gives you timed charging (Bit 1 set) and 33 gives you normal Self-Use but with grid charging enabled.
Registers needed in solis2mqtt_modbus.yaml to write a charging window and current limit.
(This means the solis will charge for the full duration of the charging window, at the lowest possible current which should be kinder to batteries)
# Writable registry entries
# Battery charge Limit
- name: write_battery_charge_limit
active: true
modbus:
register: 43141
number_of_decimals: 0
write_function_code: 6
input_type: holding
signed: false
# Write charging window
- name: write_charging_window_start_hours
active: true
modbus:
register: 43143
number_of_decimals: 0
write_function_code: 6
input_type: holding
signed: false
- name: write_charging_window_start_mins
active: true
modbus:
register: 43144
number_of_decimals: 0
write_function_code: 6
input_type: holding
signed: false
- name: write_charging_window_close_hours
active: true
modbus:
register: 43145
number_of_decimals: 0
write_function_code: 6
input_type: holding
signed: false
- name: write_charging_window_close_mins
active: true
modbus:
register: 43146
number_of_decimals: 0
write_function_code: 6
input_type: holding
signed: false
HA Script to write the charging window to the solis.
You will need to create the input_datetime helpers to set the start and end times
Settings > Devices & Services > Helpers > Create Helper > Date and/or Time > Time only
Then create the script below by pasting: Settings > Automations & Scenes > Scripts > Add New (View in YAML)
alias: Write Solis Charging Window
sequence:
- service: mqtt.publish
data:
topic: solis2mqtt/write_charging_window_start_hours/set
payload_template: "{{(states('input_datetime.charging_start_time')).split(':')[0] | int}}"
- service: mqtt.publish
data:
topic: solis2mqtt/write_charging_window_start_mins/set
payload_template: "{{(states('input_datetime.charging_start_time')).split(':')[1] | int}}"
- service: mqtt.publish
data:
topic: solis2mqtt/write_charging_window_close_hours/set
payload_template: "{{(states('input_datetime.charging_end_time')).split(':')[0] | int}}"
- service: mqtt.publish
data:
topic: solis2mqtt/write_charging_window_close_mins/set
payload_template: "{{(states('input_datetime.charging_end_time')).split(':')[1] | int}}"
mode: single
icon: mdi:clock-digital
Run that then check the charging window on the solis. Disclaimer from above post applies.
To set the charging window we need some new entities and a new helper to set the charge target
Settings > Devices & Services > Helpers > Create Helper > Number
Also make sure you are pulling the battery voltage from the solis:
- name: battery_voltage
description: Battery Voltage
unit: V
active: true
modbus:
register: 33133
read_type: register
number_of_decimals: 1
function_code: 4
signed: false
homeassistant:
device: sensor
state_class: measurement
device_class: voltage
In config.yaml create the following entities:
template:
- sensor:
####### SOLIS DYNAMIC CHARGING
- name: 'Charging Time Remaining'
icon: 'mdi:timelapse'
state: >
{% if today_at(states('input_datetime.charging_end_time')) < now() -%}
{{(((today_at(states('input_datetime.charging_end_time')) -
today_at(states('input_datetime.charging_start_time'))))
.total_seconds() / 36) | int / 100 }}
{%- else -%}
{{ max((((today_at(states('input_datetime.charging_end_time')) -
max(now(), today_at(states('input_datetime.charging_start_time'))))
.total_seconds() / 36) | int) / 100) }}
{%- endif %}
- name: 'Target Charge Limit'
icon: 'mdi:sine-wave'
unit_of_measurement: 'A'
state: >
{{ ((
(states('input_number.charge_target')|int / 100) -
(states('sensor.battery_soc')|int / 100)
) *
10000 /
states('sensor.battery_voltage')|int /
states('sensor.charging_time_remaining')|int
) | round(1)
}}
Then create the following script
alias: Set Solis Charging Current
sequence:
- service: mqtt.publish
data:
topic: solis2mqtt/write_battery_charge_limit/set
payload_template: "{{ states('sensor.target_charge_limit')|int * 10 }}"
mode: single
icon: mdi:sine-wave
Lastly, here is the automation I've used to call the two scripts every 20 mins within the charging window.
alias: Solis Overnight Inverter Charge Rate
description: >-
Set Solis Inverter overnight charge rate so that it lasts the duration of the
off peak period
trigger:
- platform: time_pattern
minutes: "0"
- platform: time_pattern
minutes: "20"
- platform: time_pattern
minutes: "40"
- platform: time
at: input_datetime.charging_start_time
condition:
- condition: time
after: input_datetime.charging_start_time
before: input_datetime.charging_end_time
action:
- service: script.set_solis_charging_current
data: {}
- service: script.write_solis_charging_window
data: {}
mode: single
Huge thanks to @fboundy, without whom I'd have never got started with this
Hi,
So I've got this pretty much reading most things I need but was wondering what modifications would be needed to write to the inverter? Ie Forced charge / discharge periods etc.
I've been following this HomeAssistant thread but not sure how this could work using this integration. I know no Python either...
Thanks for any assistance
Edit: Still trying to get this off the ground. Can anyone explain the purpose of this code, taken from solis2mqtt.py