1
0
Fork 0
ha-config/config/packages/sungrow.yaml

1570 lines
49 KiB
YAML
Raw Normal View History

2022-07-19 18:53:10 +10:00
# Sungrow integration
# Based on https://github.com/mkaiser/Sungrow-SHx-Inverter-Modbus-Home-Assistant by Martin Kaiser
# Heavily modified to work around quirks in WiNet-S and SH5.0RS firmwares
# Commented out sensors are because the registers throw errors when they are read
# - id: "24321413553543" # random number
# alias: update battery charge discharge cmd
# description: "Updates Sungrow holding register for battery charge discharge command"
# trigger:
# - platform: state
# entity_id:
# - input_select.set_sg_battery_charge_discharge_cmd
# condition: []
# action:
# - service: modbus.write_register
# data_template:
# address: 13050 # 13051
# slave: 1
# # 0xAA: 170 Charge
# # 0xBB: 187 Discharge
# # 0xCC: 204 Stop (Default)
# # don't know how to compare the integer values in hexadecimal format in following decoder statement...
# value: "{% if is_state('input_select.set_sg_battery_charge_discharge_cmd' , 'Stop (default)') %} 204
# {% elif is_state('input_select.set_sg_battery_charge_discharge_cmd' , 'Force charge') %} 170
# {% elif is_state('input_select.set_sg_battery_charge_discharge_cmd' , 'Force discharge') %} 187
# {% else %} 204 {% endif %}"
# hub: SungrowSHx
# mode: single
2022-08-24 13:50:56 +10:00
2022-08-24 16:25:05 +10:00
input_boolean:
inverter_charging_schedule_enabled:
initial: true
name: Inverter charging schedule enabled
inverter_charging_schedule_2_enabled:
initial: true
name: Inverter charging schedule 2 enabled
2022-08-24 13:50:56 +10:00
input_datetime:
inverter_charging_schedule_start:
has_time: true
name: Inverter charging schedule start
initial: "13:30:00"
inverter_charging_schedule_end:
has_time: true
name: Inverter charging schedule end
2022-08-24 16:25:05 +10:00
initial: "15:00:00"
inverter_charging_schedule_2_start:
has_time: true
name: Inverter charging schedule 2 start
initial: "00:00:00"
inverter_charging_schedule_2_end:
has_time: true
name: Inverter charging schedule 2 end
initial: "00:00:00"
2022-08-24 13:50:56 +10:00
input_number:
inverter_charging_schedule_target_soc:
2022-08-24 16:25:05 +10:00
name: Inverter charging schedule target state of charge
2022-08-24 13:50:56 +10:00
min: 0
max: 100
unit_of_measurement: "%"
2022-08-24 16:25:05 +10:00
initial: 85
mode: box
inverter_charging_schedule_2_target_soc:
name: Inverter charging schedule 2 target state of charge
min: 0
max: 100
unit_of_measurement: "%"
initial: 0
2022-08-24 13:50:56 +10:00
mode: box
inverter_battery_reserve:
name: Inverter battery reserve
min: 0
max: 100
initial: 5
unit_of_measurement: "%"
mode: box
2022-08-31 12:25:06 +10:00
inverter_forced_mode_battery_power:
name: Inverter forced mode battery power
min: 10
max: 6600
step: 10
initial: 2000
mode: box
unit_of_measurement: "W"
2022-08-24 13:50:56 +10:00
automation:
- id: e7df7dc23815acacdd8c
alias: Inverter - apply charging schedule
mode: restart
trigger:
- platform: state
entity_id:
2022-08-24 16:25:05 +10:00
# Explicit enables
- input_boolean.inverter_charging_schedule_enabled
- input_boolean.inverter_charging_schedule_2_enabled
# Schedule 1 details
2022-08-24 13:50:56 +10:00
- input_datetime.inverter_charging_schedule_start
- input_datetime.inverter_charging_schedule_end
- input_number.inverter_charging_schedule_target_soc
2022-08-24 16:25:05 +10:00
# Schedule 2 details
- input_datetime.inverter_charging_schedule_2_start
- input_datetime.inverter_charging_schedule_2_end
- input_number.inverter_charging_schedule_2_target_soc
2022-08-24 13:50:56 +10:00
not_from:
- unknown
- unavailable
not_to:
- unknown
- unavailable
2022-08-24 16:25:05 +10:00
# I have a theory that being in a schedule window prevents the battery from discharging, EVEN IF the SoC is above
# the target. As such, this automation will also run when the battery level crosses any threshold here and the
# `enabled_1` and `enabled_2` variables below will take this into consideration. In other words, the schedules
# will be automatically turned off when the charge level reaches target, and switched back on if it falls below.
- platform: numeric_state
entity_id: sensor.inverter_battery_level
above: input_number.inverter_charging_schedule_target_soc
- platform: numeric_state
entity_id: sensor.inverter_battery_level
above: input_number.inverter_charging_schedule_2_target_soc
- platform: numeric_state
entity_id: sensor.inverter_battery_level
below: input_number.inverter_charging_schedule_target_soc
- platform: numeric_state
entity_id: sensor.inverter_battery_level
below: input_number.inverter_charging_schedule_2_target_soc
variables:
2022-08-24 17:25:05 +10:00
# Enable each schedule only if battery level is below target, it is explicitly enabled, and the dates have values.
# NOTE: It would be better to take into consideration if the schedule is active (timewise) but in effect disabling
# it unconditionally when battery level is higher doesn't matter, since we also trigger when it is lower and
# it will become re-enabled again.
2022-08-24 16:25:05 +10:00
enabled_1: |
{{ is_state('input_boolean.inverter_charging_schedule_enabled', 'on') and
states('input_number.inverter_charging_schedule_target_soc') | float(default = 0) > states('sensor.inverter_battery_level') | float(default = 0) and
not is_state('input_datetime.inverter_charging_schedule_start', 'unknown') and
not is_state('input_datetime.inverter_charging_schedule_end', 'unknown')
}}
enabled_2: |
{{ is_state('input_boolean.inverter_charging_schedule_2_enabled', 'on') and
states('input_number.inverter_charging_schedule_2_target_soc') | float(default = 0) > states('sensor.inverter_battery_level') | float(default = 0) and
2022-08-24 17:25:05 +10:00
not is_state('input_datetime.inverter_charging_schedule_2_start', 'unknown') and
not is_state('input_datetime.inverter_charging_schedule_2_end', 'unknown')
2022-08-24 16:25:05 +10:00
}}
2022-08-24 13:50:56 +10:00
action:
- service: script.apply_charging_schedule
data_template:
2022-08-24 16:25:05 +10:00
soc_1: |
{% if enabled_1 %}
{{ states('input_number.inverter_charging_schedule_target_soc') | int }}
{% else %}
0
{% endif %}
start_time_1: "{{ states('input_datetime.inverter_charging_schedule_start') | replace('unknown','00:00') }}"
end_time_1: "{{ states('input_datetime.inverter_charging_schedule_end') | replace('unknown','00:00') }}"
soc_2: |
{% if enabled_2 %}
{{ states('input_number.inverter_charging_schedule_2_target_soc') | int }}
{% else %}
0
{% endif %}
2022-08-24 17:25:05 +10:00
start_time_2: "{{ states('input_datetime.inverter_charging_schedule_2_start') | replace('unknown','00:00') }}"
end_time_2: "{{ states('input_datetime.inverter_charging_schedule_2_end') | replace('unknown','00:00') }}"
2022-08-24 13:50:56 +10:00
- id: 7b93d325bfe632a4e890
alias: Inverter - update battery reserve
mode: restart
trigger:
- platform: state
entity_id:
- input_number.inverter_battery_reserve
not_from:
- unknown
- unavailable
not_to:
- unknown
- unavailable
action:
- service: script.inverter_set_battery_reserve
data_template:
reserved_percentage: >-
{{ states('input_number.inverter_battery_reserve') | int }}
2022-08-31 12:25:06 +10:00
- id: 647fd6b77b7f72f0bf75
alias: Inverter - set forced mode battery power
mode: restart
trigger:
- platform: state
entity_id:
- input_number.inverter_forced_mode_battery_power
not_from:
- unknown
- unavailable
not_to:
- unknown
- unavailable
variables:
current_power: "{{ states('sensor.inverter_battery_forced_charge_discharge_power') | int }}"
target_power: "{{ states('input_number.inverter_forced_mode_battery_power') | int }}"
target_power_value: "{{ target_power / 10 }}" # undocumented register is in multiples of 10W
condition:
- "{{ current_power != target_power }}"
action:
- service: modbus.write_register
data_template:
# Documented modbus address 13051 is readable but won't write.
# address: 13050 # 13051
# However, there is an undocumented register 33148 which takes a number in multiples of 10W which works.
address: 33147 # 33148
slave: 1
value: "{{ [target_power_value] }}"
hub: SungrowSHx
- id: 31fd5a1552832c9a14c9
alias: Inverter - update forced mode battery power
mode: restart
trigger:
- platform: state
entity_id:
- sensor.inverter_battery_forced_charge_discharge_power
not_from:
- unknown
- unavailable
not_to:
- unknown
- unavailable
action:
- service: input_number.set_value
target:
entity_id: input_number.inverter_forced_mode_battery_power
data_template:
value: "{{ states('sensor.inverter_battery_forced_charge_discharge_power') | int }}"
2022-08-26 10:25:05 +10:00
# The inverter can only generate 5kW of AC power. IFF the battery is not-full, it can generate 5kW of AC _and_ charge
# battery at up to 6.6kW, which saturates our 8.46 kW of PV
#
# So, preventing the battery from getting full too early on a sunny day will increase our overall yield, because the
# inverter won't have to derate the PV power to 5kW.
#
# [1]: https://discord.com/channels/936031869001158666/1008992991643455529
# [2]: https://discord.com/channels/936031869001158666/936031869001158669/1011882768021590036
- id: 05bdc8eb58714c26c2fe
alias: Inverter - maximise output
mode: restart
trigger:
2022-08-26 11:25:07 +10:00
- platform: state
2022-08-26 10:25:05 +10:00
entity_id:
2022-08-31 13:25:05 +10:00
- sensor.inverter_pv_power
2022-08-26 10:25:05 +10:00
- sensor.inverter_battery_level
- sensor.inverter_active_power
- sensor.home_weather_cloud_coverage
- sensor.home_weather_forecast_cloud_coverage
- sensor.solcast_forecast_remaining_today
2022-08-26 11:25:07 +10:00
# - sensor.home_weather_forecast_condition
# - sensor.home_weather_condition
# - weather.home
- weather.home_hourly # can use attributes on this one to make decisions about the coming hours
# - weather.home_weather
- sun.sun # use `next_setting` attribute to ensure battery is online at least an hour before sunset
not_to:
- unavailable
- unknown
2022-08-26 10:25:05 +10:00
variables:
2022-08-31 13:25:05 +10:00
# Magic numbers
ems_self_consume: 0
ems_forced: 2
battery_charge: 0xAA
battery_discharge: 0xBB
battery_stop: 0xCC
active_power_limit: 4999 # W
active_power_buffer: 200 # W - how much below limit we want to sit
battery_lower_limit: 50 # %
2022-08-31 14:25:05 +10:00
battery_upper_limit: 98 # % - above this, let the BMS choose the charge rate
2022-08-31 13:25:05 +10:00
battery_capacity: 12.8 # kWh
# Shorthands
2022-08-31 14:00:40 +10:00
current_active_power: "{{ states('sensor.inverter_active_power') | int(default=active_power_limit) }}"
current_pv_power: "{{ states('sensor.inverter_pv_power') | int(default=0) }}"
2022-08-31 13:25:05 +10:00
current_ems_mode: "{{ states('sensor.inverter_ems_mode_raw') | int(default=-1) }}"
current_battery_mode: "{{ states('sensor.inverter_forced_battery_mode_raw') | int(default=0) }}"
2022-08-31 14:25:05 +10:00
battery_level: "{{ states('sensor.inverter_battery_level') | float(default=100) }}"
2022-08-31 14:00:40 +10:00
forced_battery_power: "{{ states('sensor.inverter_battery_forced_charge_discharge_power') | int(default=0) }}"
forecast_remaining: "{{ states('sensor.solcast_forecast_remaining_today') | float(default=0) }}"
2022-08-31 13:25:05 +10:00
target_active_power: "{{ active_power_limit - active_power_buffer }}"
2022-08-31 14:00:40 +10:00
desired_forced_battery_power: >
{% if current_pv_power > target_active_power %}
{{ current_pv_power - target_active_power }}
{% else %}
10
{% endif %}
2022-08-31 13:25:05 +10:00
is_forced_charging: >
{{ current_ems_mode == ems_forced and current_battery_mode == battery_charge }}
is_self_consuming: >
{{ current_ems_mode == ems_self_consume }}
2022-08-26 10:25:05 +10:00
kwh_until_full: >
2022-08-31 13:25:05 +10:00
{{ battery_capacity * ((100 - battery_level)/100) }}
enough_in_day: >
2022-08-31 14:00:40 +10:00
{{ 2.5 * kwh_until_full < forecast_remaining }}
2022-08-31 13:25:05 +10:00
battery_high_enough: "{{ battery_level > battery_lower_limit }}"
2022-08-31 14:25:05 +10:00
battery_too_high: "{{ battery_level >= battery_upper_limit }}"
2022-08-26 11:25:07 +10:00
sunsetting: >
2022-08-31 14:00:40 +10:00
{{ now() + timedelta(hours = 1) > state_attr('sun.sun', 'next_setting') | as_datetime }}
2022-08-26 11:25:07 +10:00
should_slow_battery: >
2022-08-31 14:25:05 +10:00
{{ not sunsetting and battery_high_enough and enough_in_day and not battery_too_high }}
2022-08-26 11:25:07 +10:00
action:
2022-08-31 14:00:40 +10:00
# TODO: discharge battery if too high and PV has dropped while forecast remains high
2022-08-26 11:25:07 +10:00
- choose:
- conditions:
2022-08-31 14:00:40 +10:00
- "{{ should_slow_battery }}"
2022-08-26 11:25:07 +10:00
sequence:
2022-08-31 14:00:40 +10:00
- service: input_number.set_value
target:
entity_id: input_number.inverter_forced_mode_battery_power
data_template:
value: "{{ desired_forced_battery_power }}"
- condition: "{{ is_self_consuming or not is_forced_charging }}"
- service: script.inverter_force_battery_charge
2022-08-26 11:25:07 +10:00
- conditions:
2022-08-31 14:00:40 +10:00
- not:
- "{{ is_self_consuming }}"
2022-08-26 11:25:07 +10:00
sequence:
- service: script.inverter_self_consumption
default: []
2022-08-26 10:25:05 +10:00
2022-07-19 18:53:10 +10:00
script:
2022-08-24 13:50:56 +10:00
inverter_set_battery_reserve:
alias: "Set inverter battery reserve"
mode: restart
fields:
reserved_percentage:
name: "Percentage"
description: "Percentage of battery reserved for emergency power supply"
required: true
selector:
number:
min: 0
max: 100
step: 1
unit_of_measurement: "%"
sequence:
- service: modbus.write_register
data_template:
address: 13099 # 13100
slave: 1
value: "{{ [ reserved_percentage | int ] }}"
hub: SungrowSHx
# inverter_set_battery_mode:
# alias: Set inverter battery mode
# fields:
# battery_power:
# name: "Charge/discharge power"
# description: "Charge/discharge power"
# required: false
# selector:
# number:
# min: 0
# max: 6600
# step: 1
# unit_of_measurement: "W"
# battery_mode:
# name: "Battery mode"
# description: "Battery mode"
# required: true
# selector:
# select:
# options:
# - label: Self-consumption
# value: [0, 0xCC]
# - label: Force charge
# value: [2, 0xAA]
# - label: Force discharge
# value: [2, 0xBB]
# - label: Stop
# value: [2, 0xCC]
# sequence:
# - service: modbus.write_register
# data_template:
# address: 13049 # 13050-13051
# slave: 1
# # value: "{{ battery_mode }}"
# value: |
# {% if battery_power %}
# {{ battery_mode + [battery_power|int] }}
# {% else %}
# {{ battery_mode }}
# {% endif %}
# hub: SungrowSHx
2022-07-19 18:53:10 +10:00
inverter_force_battery_charge:
alias: "Inverter - force battery charge"
2022-08-24 13:50:56 +10:00
mode: restart
2022-07-19 18:53:10 +10:00
sequence:
- service: modbus.write_register
data_template:
address: 13049 # 13050-13051
slave: 1
value: [2, 0xAA]
hub: SungrowSHx
inverter_force_battery_discharge:
alias: "Inverter - force battery discharge"
2022-08-24 13:50:56 +10:00
mode: restart
2022-07-19 18:53:10 +10:00
sequence:
- service: modbus.write_register
data_template:
address: 13049 # 13050-13051
slave: 1
value: [2, 0xBB]
hub: SungrowSHx
inverter_force_battery_stop:
alias: "Inverter - force battery stop"
2022-08-24 13:50:56 +10:00
mode: restart
2022-07-19 18:53:10 +10:00
sequence:
- service: modbus.write_register
data_template:
address: 13049 # 13050-13051
slave: 1
value: [2, 0xCC]
hub: SungrowSHx
inverter_self_consumption:
alias: "Inverter - self consumption"
2022-08-24 13:50:56 +10:00
mode: restart
2022-07-19 18:53:10 +10:00
sequence:
- service: modbus.write_register
data_template:
address: 13049 # 13050-13051
slave: 1
value: [0, 0xCC]
hub: SungrowSHx
2022-08-24 13:50:56 +10:00
# https://github.com/bohdan-s/SunGather/pull/63#issuecomment-1195049341
apply_charging_schedule:
alias: "Inverter - apply charging schedule"
# Q: is this evaluated _after_ field values are captured or before?
variables:
enable_value: 0xAA
disable_value: 0x55
every_day_value: 1
is_enabled: "{{ (soc_1 is defined and soc_1 | int > 0) or (soc_2 is defined and soc_2 | int > 0) }}"
sched_1: >
{% if soc_1 is defined and soc_1 | int > 0 %}
{{ [
today_at(start_time_1).hour,
today_at(start_time_1).minute,
today_at(end_time_1).hour,
today_at(end_time_1).minute,
soc_1
] }}
{% else %}
{{ [0, 0, 0, 0, 0] }}
{% endif %}
sched_2: >
{% if soc_2 is defined and soc_2 | int > 0 %}
{{ [
today_at(start_time_2).hour,
today_at(start_time_2).minute,
today_at(end_time_2).hour,
today_at(end_time_2).minute,
soc_2
] }}
{% else %}
{{ [0, 0, 0, 0, 0] }}
{% endif %}
bytes: >
{% if is_enabled %}
{{ [enable_value, every_day_value] + sched_1 + sched_2 }}
{% else %}
{{ [disable_value] }}
{% endif %}
fields:
start_time_1:
name: Schedule 1 Start Time
default: "13:30:00"
selector:
time:
end_time_1:
name: Schedule 1 End Time
default: "14:59:00" # 3PM is on-peak charging
selector:
time:
soc_1:
name: Schedule 1 Target State-of-Charge
default: 85
selector:
number:
min: 0
max: 100
unit_of_measurement: "%"
start_time_2:
name: Schedule 2 Start Time
default: "0:00:00"
selector:
time:
end_time_2:
name: Schedule 2 End Time
default: "00:00:00"
selector:
time:
soc_2:
name: Schedule 2 Target State-of-Charge
default: 0
selector:
number:
min: 0
max: 100
unit_of_measurement: "%"
sequence:
- service: modbus.write_register
data_template:
address: 33207 # 33208 - ...
slave: 1
value: "{{ bytes | list }}"
hub: SungrowSHx
2022-07-19 18:53:10 +10:00
modbus:
- name: SungrowSHx
type: tcp
host: !secret solar_inverter_ip
port: 502
retry_on_empty: true
# retries: 10
# close_comm_on_error: true
delay: 5
#timeout: 5
sensors:
# - name: Sungrow Device type code
# slave: 1
# address: 4999 # 5000
# input_type: input
# count: 1
# data_type: uint16
# swap: word
# scan_interval: 60
- name: Inverter output energy today
unique_id: d1a3ad60e978aaea9407
slave: 1
address: 5002
input_type: input
count: 1
data_type: uint16
swap: word
precision: 1
unit_of_measurement: kWh
device_class: energy
state_class: total_increasing
scale: 0.1
scan_interval: 60
- name: Inverter output energy total
unique_id: e28068b742fd20919aea
slave: 1
address: 5003
input_type: input
count: 2
data_type: uint32
swap: word
unit_of_measurement: kWh
precision: 1
device_class: energy
state_class: total_increasing
scale: 0.1
scan_interval: 60
- name: Inverter temperature
unique_id: a29ef61bc181d9b95ef7
slave: 1
address: 5007
input_type: input
count: 1
data_type: int16
precision: 1
unit_of_measurement: °C
device_class: temperature
state_class: measurement
scale: 0.1
scan_interval: 10
- name: Inverter grid frequency
unique_id: 489e85158f95e8bd71fb
slave: 1
address: 5035
input_type: input
count: 1
data_type: uint16
swap: word
precision: 2
unit_of_measurement: Hz
device_class: frequency
state_class: measurement
scale: 0.01 # docs say 0.1 but WiNet-S implementation differs
scan_interval: 10
- name: Inverter phase A voltage
unique_id: c892eb3b2a51b37ffab3
slave: 1
address: 5018
input_type: input
count: 1
data_type: uint16
swap: word
precision: 1
unit_of_measurement: V
device_class: voltage
state_class: measurement
scale: 0.1
scan_interval: 10
- name: Inverter phase A current
unique_id: 5963c0d0bf24ad5412ff
slave: 1
address: 13030
input_type: input
count: 1
data_type: int16
swap: word
precision: 1
unit_of_measurement: A
device_class: current
state_class: measurement
scale: 0.1
scan_interval: 10
- name: Inverter MPPT1 voltage
unique_id: 024b2df03fb2d724277c
slave: 1
address: 5010
input_type: input
count: 1
data_type: uint16
swap: word
precision: 1
unit_of_measurement: V
device_class: voltage
state_class: measurement
scale: 0.1
scan_interval: 10
- name: Inverter MPPT1 current
unique_id: a5179dc512ce97cd1543
slave: 1
address: 5011
input_type: input
count: 1
data_type: uint16
swap: word
precision: 2
unit_of_measurement: A
device_class: current
scale: 0.1
scan_interval: 10
- name: Inverter MPPT2 voltage
unique_id: 0b856f1678a3afb1002f
slave: 1
address: 5012
input_type: input
count: 1
data_type: uint16
swap: word
precision: 1
unit_of_measurement: V
device_class: voltage
state_class: measurement
scale: 0.1
scan_interval: 10
- name: Inverter MPPT2 current
unique_id: f732afc7332efa49be11
slave: 1
address: 5013
input_type: input
count: 1
data_type: uint16
swap: word
precision: 2
unit_of_measurement: A
device_class: current
state_class: measurement
scale: 0.1
scan_interval: 10
- name: Inverter PV power
unique_id: 4af1ff47ed7fd1bad213
slave: 1
address: 5016
input_type: input
count: 2
data_type: uint32
swap: word
precision: 0
unit_of_measurement: W
device_class: power
state_class: measurement
scale: 1
2022-08-26 11:25:07 +10:00
scan_interval: 5
2022-07-19 18:53:10 +10:00
- name: Inverter active power
unique_id: a3ee41f78c6c55377c00
slave: 1
address: 13033
input_type: input
count: 2
data_type: int32
swap: word
precision: 0
unit_of_measurement: W
device_class: power
state_class: measurement
scale: 1
2022-08-26 11:25:07 +10:00
scan_interval: 5
2022-07-19 18:53:10 +10:00
- name: Inverter reactive power
unique_id: 5a58fe975877eec6d37c
slave: 1
address: 5032
input_type: input
count: 2
data_type: int32
swap: word
precision: 0
unit_of_measurement: var
device_class: reactive_power
state_class: measurement
scale: 1
scan_interval: 10
- name: Inverter power factor
unique_id: 29099a467ed9fec02cb1
slave: 1
address: 5034
input_type: input
count: 1
data_type: int16
swap: word
unit_of_measurement: "%"
device_class: power_factor
state_class: measurement
# scale: 0.001 # according to docs...
scale: 0.1
# precision: 3
precision: 1
scan_interval: 10
# - name: BDC rated power
# slave: 1
# address: 5627 #5628
# input_type: input
# count: 1
# data_type: uint16
# swap: word
# unit_of_measurement: "W"
# device_class: power
# state_class: measurement
# scale: 100
# scan_interval: 10
2022-08-25 12:25:05 +10:00
- name: Inverter system state (raw)
slave: 1
address: 12999
input_type: input
count: 1
data_type: uint16
swap: word
precision: 0
scale: 1
2022-08-26 11:25:07 +10:00
scan_interval: 5
2022-07-19 18:53:10 +10:00
2022-08-25 12:25:05 +10:00
# Currently this only ever returns 0x00 over Modbus :/
- name: Inverter running state (raw)
slave: 1
address: 13000
input_type: input
count: 1
data_type: uint16
swap: word
precision: 0
scale: 1
2022-08-26 11:25:07 +10:00
scan_interval: 5
2022-07-19 18:53:10 +10:00
- name: Inverter PV generation today
unique_id: 27953d57c315260a7983
slave: 1
address: 13001
input_type: input
count: 1
data_type: uint16
swap: word
precision: 1
unit_of_measurement: kWh
device_class: energy
state_class: total_increasing
scale: 0.1
scan_interval: 60
- name: Inverter PV generation total
unique_id: 885db873746c92fcdf2d
slave: 1
address: 13002
input_type: input
count: 2
data_type: uint32
swap: word
precision: 1
unit_of_measurement: kWh
device_class: energy
state_class: total_increasing
scale: 0.1
scan_interval: 60
- name: Inverter exported energy from PV today
unique_id: 08454b6e9f7061180795
slave: 1
address: 13004
input_type: input
count: 1
data_type: uint16
swap: word
precision: 1
unit_of_measurement: kWh
device_class: energy
state_class: total_increasing
scale: 0.1
scan_interval: 60
- name: Inverter exported energy from PV total
unique_id: 00efcf01e33337d01b5f
slave: 1
address: 13005
input_type: input
count: 2
data_type: uint32
swap: word
precision: 1
unit_of_measurement: kWh
device_class: energy
state_class: total_increasing
scale: 0.1
scan_interval: 60
- name: Inverter load power
unique_id: 9777fe694d687a4f30df
slave: 1
address: 13007
input_type: input
count: 2
data_type: int32
swap: word
precision: 0
unit_of_measurement: W
device_class: power
state_class: measurement
scale: 1
2022-08-26 11:25:07 +10:00
scan_interval: 5
2022-07-19 18:53:10 +10:00
- name: Inverter export power
unique_id: 4c3bac29a87561796c06
slave: 1
address: 13009
input_type: input
count: 2
data_type: int32
swap: word
precision: 0
unit_of_measurement: W
device_class: power
state_class: measurement
scale: 1
2022-08-26 11:25:07 +10:00
scan_interval: 5
2022-07-19 18:53:10 +10:00
- name: Inverter battery charge from PV today
unique_id: d496459e40e0d49f5c11
slave: 1
address: 13011
input_type: input
count: 1
data_type: uint16
swap: word
precision: 1
unit_of_measurement: kWh
device_class: energy
state_class: total_increasing
scale: 0.1
scan_interval: 60
- name: Inverter battery charge from PV total
unique_id: d743fb021ad5ffd4eaec
slave: 1
address: 13012
input_type: input
count: 2
data_type: uint32
swap: word
precision: 1
unit_of_measurement: kWh
device_class: energy
state_class: total_increasing
scale: 0.1
scan_interval: 60
- name: Inverter direct energy consumption today
unique_id: 31666be0c3b9c9b1046c
slave: 1
address: 13016
input_type: input
count: 1
data_type: uint16
swap: word
precision: 1
unit_of_measurement: kWh
device_class: energy
state_class: total_increasing
scale: 0.1
scan_interval: 60
- name: Inverter direct energy consumption total
unique_id: 1e52906bf8fb43bd584e
slave: 1
address: 13017
input_type: input
count: 2
data_type: uint32
swap: word
precision: 1
unit_of_measurement: kWh
device_class: energy
state_class: total_increasing
scale: 0.1
scan_interval: 60
- name: Inverter battery voltage
unique_id: d847a32343d531d39de9
slave: 1
address: 13019
input_type: input
count: 1
data_type: uint16
swap: word
precision: 0
unit_of_measurement: V
device_class: voltage
state_class: measurement
scale: 0.1
scan_interval: 10
- name: Inverter battery current
unique_id: 211c77af04532163e7b0
slave: 1
address: 13020
input_type: input
count: 1
# data_type: uint16
data_type:
int16 # docs say unsigned but I'm getting overflows when battery is charging
# changing to int shows negative amperage which makes sense.
swap: word
precision: 1
unit_of_measurement: A
device_class: current
state_class: measurement
scale: 0.1
2022-08-26 11:25:07 +10:00
scan_interval: 5
2022-07-19 18:53:10 +10:00
- name: Inverter battery power (raw)
unique_id: 4f7dacbf06d004b547d9
slave: 1
address: 13021
input_type: input
count: 1
data_type: uint16
swap: word
precision: 0
unit_of_measurement: W
device_class: power
state_class: measurement
scale: 1
2022-08-26 11:25:07 +10:00
scan_interval: 5
2022-07-19 18:53:10 +10:00
- name: Inverter battery level
unique_id: 61e9d0508f0e75a3f25c
slave: 1
address: 13022
input_type: input
count: 1
data_type: uint16
swap: word
precision: 1
unit_of_measurement: "%"
device_class: battery
state_class: measurement
scale: 0.1
scan_interval: 60
- name: Inverter battery health
slave: 1
address: 13023
input_type: input
count: 1
data_type: uint16
swap: word
precision: 0
unit_of_measurement: "%"
state_class: measurement
scale: 0.1
scan_interval: 10
- name: Inverter battery temperature
slave: 1
address: 13024
input_type: input
count: 1
data_type: int16
precision: 1
unit_of_measurement: °C
device_class: temperature
state_class: measurement
scale: 0.1
scan_interval: 60
- name: Inverter battery discharge today
unique_id: aa16dda43d9420767429
slave: 1
address: 13025
input_type: input
count: 1
data_type: uint16
swap: word
precision: 1
unit_of_measurement: kWh
device_class: energy
state_class: total_increasing
scale: 0.1
scan_interval: 60
- name: Inverter battery discharge total
unique_id: c69ba5e6d4b5233e1d4b
slave: 1
address: 13026
input_type: input
count: 2
data_type: uint32
swap: word
precision: 1
unit_of_measurement: kWh
device_class: energy
state_class: total_increasing
scale: 0.1
scan_interval: 10
2022-08-25 12:25:05 +10:00
# NOTE: this is broken over modbus and only ever returns 0x00
2022-07-19 18:53:10 +10:00
# - name: Grid state raw
# slave: 1
# address: 13029
# input_type: input
# count: 1
# data_type: uint16
# swap: word
# precision: 0
# scan_interval: 10
- name: Inverter imported energy today
unique_id: b2b294f51055baee4d83
slave: 1
address: 13035
input_type: input
count: 1
data_type: uint16
swap: word
precision: 1
unit_of_measurement: kWh
device_class: energy
state_class: total_increasing
scale: 0.1
scan_interval: 60
- name: Inverter imported energy total
unique_id: 3b0efbeb671df362890c
slave: 1
address: 13036
input_type: input
count: 2
data_type: uint32
swap: word
precision: 1
unit_of_measurement: kWh
device_class: energy
state_class: total_increasing
scale: 0.1
scan_interval: 60
- name: Inverter battery charge today
unique_id: 88a0d3dadb8aa103728d
slave: 1
address: 13039
input_type: input
count: 1
data_type: uint16
swap: word
precision: 1
unit_of_measurement: kWh
device_class: energy
state_class: total_increasing
scale: 0.1
scan_interval: 60
- name: Inverter battery charge total
unique_id: ac30fabc192dd714df73
slave: 1
address: 13040
input_type: input
count: 2
data_type: uint32
swap: word
precision: 1
unit_of_measurement: kWh
device_class: energy
state_class: total_increasing
scale: 0.1
scan_interval: 60
- name: Inverter exported energy today
unique_id: 68e619a9b5ee8d444486
slave: 1
address: 13044
input_type: input
count: 1
data_type: uint16
swap: word
precision: 1
unit_of_measurement: kWh
device_class: energy
state_class: total_increasing
scale: 0.1
scan_interval: 60
- name: Inverter exported energy total
unique_id: 6a4ef5fdd89be709656a
slave: 1
address: 13045
input_type: input
count: 2
data_type: uint32
swap: word
precision: 1
unit_of_measurement: kWh
device_class: energy
state_class: total_increasing
scale: 0.1
scan_interval: 60
# # holding registers
# - name: Inverter Start Stop
# slave: 1
# address: 12999 # 13000
# input_type: holding
# count: 1
# data_type: uint16
# swap: word
# precision: 0
# scan_interval: 10
2022-08-31 13:25:05 +10:00
- name: Inverter EMS mode (raw)
unique_id: e48a4229b845475df29e
slave: 1
address: 13049 # 13050
input_type: holding
count: 1
data_type: uint16
swap: word
scan_interval: 10
- name: Inverter forced battery mode (raw)
unique_id: d1158bbfa40d73933a72
slave: 1
address: 13050 # 13051
input_type: holding
count: 1
data_type: uint16
swap: word
precision: 0
scan_interval: 10
2022-07-19 18:53:10 +10:00
2022-08-25 12:25:05 +10:00
- name: Inverter battery forced charge/discharge power
slave: 1
address: 13051 # 13052
input_type: holding
count: 1
data_type: uint16
swap: word
precision: 0
# datasheet says:
# 0 to 5000 W for SH*K-*
# 0 to 100 % for SH*.0RT
# for my SH10RT it is set in W...
unit_of_measurement: W
device_class: power
state_class: measurement
scan_interval: 10
- name: Inverter max SoC
unique_id: c602a1446bddc9d6fa7a
slave: 1
address: 13057 # 13058
input_type: holding
count: 1
data_type: uint16
swap: word
precision: 1
unit_of_measurement: "%"
device_class: battery
state_class: measurement
scale: 0.1
scan_interval: 60
- name: Inverter min SoC
unique_id: 61a08ee663d37248f372
slave: 1
address: 13058 # 13059
input_type: holding
count: 1
data_type: uint16
swap: word
precision: 1
unit_of_measurement: "%"
device_class: battery
state_class: measurement
scale: 0.1
scan_interval: 60
2022-07-19 18:53:10 +10:00
# binary_sensor:
# - platform: template
# sensors:
# pv_generating:
# friendly_name: "PV generating"
# value_template: "{{ states('sensor.running_state')|int|bitwise_and(0x1) > 0 }}"
# battery_charging:
# friendly_name: "Battery charging"
# value_template: "{{ states('sensor.running_state')|int|bitwise_and(0x2) > 0 }}"
# battery_discharging:
# friendly_name: "Battery discharging"
# value_template: "{{ states('sensor.running_state')|int|bitwise_and(0x4) > 0 }}"
# exporting_power:
# friendly_name: "Exporting power"
# value_template: "{{ states('sensor.running_state')|int|bitwise_and(0x10) > 0 }}"
# importing_power:
# friendly_name: "Importing power"
# value_template: "{{ states('sensor.running_state')|int|bitwise_and(0x20) > 0 }}"
# # 'virtual' template sensors for better readability
sensor:
- platform: template
sensors:
inverter_battery_power:
unique_id: 7ef0fbbb0d9825b99f35
friendly_name: Inverter battery power
value_template: >-
{% set current = states('sensor.inverter_battery_current') %}
{% set power = states('sensor.inverter_battery_power_raw') %}
{% if 'unavailable' in [current, power] %}
unavailable
2022-08-25 12:25:05 +10:00
{% elif 'unknown' in [current, power] %}
unknown
2022-07-19 18:53:10 +10:00
{% elif current|float < 0.0 and power|float > 0.0 %}
{{ -1 * power | float }}
{% else %}
{{ power | float }}
{% endif %}
unit_of_measurement: W
device_class: power
2022-08-25 12:25:05 +10:00
inverter_state:
# Inverter States from modbus reference manual
friendly_name: "Inverter state"
value_template: >-
{% if ((states('sensor.inverter_system_state_raw') | int(default=0)) == 0x0002) %}
Stop
{% elif ((states('sensor.inverter_system_state_raw') | int(default=0)) == 0x0008) %}
Standby
{% elif ((states('sensor.inverter_system_state_raw') | int(default=0)) == 0x0010) %}
Initial Standby
{% elif ((states('sensor.inverter_system_state_raw') | int(default=0)) == 0x0020) %}
Startup
{% elif ((states('sensor.inverter_system_state_raw') | int(default=0)) == 0x0040) %}
Running
{% elif ((states('sensor.inverter_system_state_raw') | int(default=0)) == 0x0100) %}
Fault
{% elif ((states('sensor.inverter_system_state_raw') | int(default=0)) == 0x0400) %}
Maintain mode
{% elif ((states('sensor.inverter_system_state_raw') | int(default=0)) == 0x0800) %}
Forced mode
{% elif ((states('sensor.inverter_system_state_raw') | int(default=0)) == 0x8200) %}
Dispatch running {# what even is this? #}
{% elif ((states('sensor.inverter_system_state_raw') | int(default=0)) == 0x1000) %}
Off-grid mode
{% elif ((states('sensor.inverter_system_state_raw') | int(default=0)) == 0x2501) %}
Restarting
{% elif ((states('sensor.inverter_system_state_raw') | int(default=0)) == 0x4000) %}
External EMS mode
{% else %}
unknown {#- should not see this #}
{% endif %}
2022-07-19 18:53:10 +10:00
# sungrow_grid_state:
# friendly_name: "Sungrow Grid State"
# value_template: >-
# {% if ((states('sensor.grid_state_raw') | int(default=0)) == 0x00AA) %}
# Off Grid
# {% elif ((states('sensor.grid_state_raw') | int(default=0)) == 0x0055) %}
# On Grid
# {% else %}
# Unknown - should not see me!
# {% endif %}
# sungrow_device_type:
# # device codes from modbus reference manual
# friendly_name: "Sungrow Device Type"
# value_template: >-
# {% if ((states('sensor.sungrow_device_type_code') | int(default=0)) == 0x0D09) %}
# SH5K-20
# {% elif ((states('sensor.sungrow_device_type_code') | int(default=0)) == 0x0D06) %}
# SH3K6
# {% elif ((states('sensor.sungrow_device_type_code') | int(default=0)) == 0x0D07) %}
# SH4K6
# {% elif ((states('sensor.sungrow_device_type_code') | int(default=0)) == 0x0D03) %}
# SH5K-V13
# {% elif ((states('sensor.sungrow_device_type_code') | int(default=0)) == 0x0D0C) %}
# SH5K-30
# {% elif ((states('sensor.sungrow_device_type_code') | int(default=0)) == 0x0D0A) %}
# SH3K6-30
# {% elif ((states('sensor.sungrow_device_type_code') | int(default=0)) == 0x0D0B) %}
# SH4K6-30
# {% elif ((states('sensor.sungrow_device_type_code') | int(default=0)) == 0x0D0F) %}
# SH5.0RS
# {% elif ((states('sensor.sungrow_device_type_code') | int(default=0)) == 0x0D0D) %}
# SH3.6RS
# {% elif ((states('sensor.sungrow_device_type_code') | int(default=0)) == 0x0D0E) %}
# SH4.6RS
# {% elif ((states('sensor.sungrow_device_type_code') | int(default=0)) == 0x0D10) %}
# SH6.0RS
# {% elif ((states('sensor.sungrow_device_type_code') | int(default=0)) == 0x0E03) %}
# SH10RT
# {% elif ((states('sensor.sungrow_device_type_code') | int(default=0)) == 0x0E02) %}
# SH8.0RT
# {% elif ((states('sensor.sungrow_device_type_code') | int(default=0)) == 0x0E01) %}
# SH6.0RT
# {% elif ((states('sensor.sungrow_device_type_code') | int(default=0)) == 0x0E00) %}
# SH5.0RT
# {% else %}
# Unknown device code!
# {% endif %}
# # make the sensor battery_charge_discharge_cmd more human readable
# sungrow_battery_charge_discharge_cmd:
# # Inverter States from modbus reference manual
# # 0xAA (170) Force charge
# # 0xBB (187) Force discharge
# # 0xCC (204) Stop (Default)
# # don't know how to compare the integer values in hexadecimal format in following decoder statement...
# friendly_name: "Battery Charge Discharge Cmd"
# value_template: >-
# {% if ((states('sensor.battery_charge_discharge_cmd_raw') | int(default=0)) == 0x00AA) %}
# Force charge
# {% elif ((states('sensor.battery_charge_discharge_cmd_raw') | int(default=0)) == 0x00BB) %}
# Force discharge
# {% elif ((states('sensor.battery_charge_discharge_cmd_raw') | int(default=0)) == 0x00CC) %}
# Stop (Default)
# {% else %}
# Unknown - should not see me!
# {% endif %}
# # make the sensor ems_selection_raw more human readable
# sungrow_ems_mode_selection:
# friendly_name: "EMS Mode Selection"
# value_template: >-
# {% if ((states('sensor.ems_mode_selection_raw') | int(default=0)) == 0) %}
# Self-consumption mode (default)
# {% elif ((states('sensor.ems_mode_selection_raw') | int(default=0)) == 2) %}
# Forced mode
# {% elif ((states('sensor.ems_mode_selection_raw') | int(default=0)) == 3) %}
# External EMS
# {% elif ((states('sensor.ems_mode_selection_raw') | int(default=0)) == 4) %}
# VPP
# {% else %}
# Unknown - should not see me!
# {% endif %}
# # getting input for Min and Max SoC
# input_number:
# set_sg_min_soc:
# name: min Soc
# #initial: 15
# min: 5
# max: 50
# step: 1
# set_sg_max_soc:
# name: max Soc
# #initial: 85
# min: 50
# max: 95
# step: 1
# set_sg_charge_discharge_power_percentage:
# name: max charge discharge power in W
# #initial: 40
# min: 0
# max: 5000
# step: 50
# input_select:
# set_sg_start_stop_mode:
# name: Inverter mode
# options:
# - "Start"
# - "Stop"
# # get input for battery mode (forced charge/discharge, stop (default) )
# set_sg_ems_mode:
# name: EMS mode
# options:
# - "Self-consumption mode (default)"
# - "Forced mode"
# - "External EMS"
# - "VPP"
# icon: mdi:battery-unknown
# set_sg_battery_charge_discharge_cmd:
# name: Battery charge discharge cmd
# options:
# - "Stop (default)"
# - "Force charge"
# - "Force discharge"
# icon: mdi:battery-unknown
# # Automation: Write modbus registers on input changes via GUI
# # note: If you change a value by the sliders, it will take up to 60 seconds until the state variables are updated
# # Unfortunately, I could not find a way to "force update" modbus registers, yet...
# automation:
# # Start 0xCF = 207
# # Stop 0xCE = 206
# # Todo I want to use hexadecimal format to write values!?
# - id: "24334413543545" # random number
# alias: start stop
# description: "Starts/ Stops the inverter"
# trigger:
# - platform: state
# entity_id:
# - input_select.set_sg_start_stop_mode
# condition: []
# action:
# - service: modbus.write_register
# data_template:
# address: 12999 # 13000
# slave: 1
# value: "{% if is_state('input_select.set_sg_start_stop_mode' , 'Start') %} 207
# {% else %} 206 {% endif %}"
# hub: SungrowSHx
# mode: single
# - id: "24321413543543" # random number
# alias: update max SoC
# description: "Updates Sungrow max Soc holding register"
# trigger:
# - platform: state
# entity_id:
# - input_number.set_sg_max_soc
# condition: []
# action:
# - service: modbus.write_register
# data_template:
# address: 13057 # 13058
# slave: 1
# value: "{{ states('input_number.set_sg_max_soc') | int *10}}"
# hub: SungrowSHx
# mode: single
# - id: "24321413543545" # random number
# alias: update min SoC
# description: "Updates Sungrow min Soc holding register"
# trigger:
# - platform: state
# entity_id:
# - input_number.set_sg_min_soc
# condition: []
# action:
# - service: modbus.write_register
# data_template:
# address: 13058 # 13059
# slave: 1
# value: "{{ states('input_number.set_sg_min_soc') | int *10}}"
# hub: SungrowSHx
# mode: single
# - id: "24321413553543" # random number
# alias: update battery charge discharge cmd
# description: "Updates Sungrow holding register for battery charge discharge command"
# trigger:
# - platform: state
# entity_id:
# - input_select.set_sg_battery_charge_discharge_cmd
# condition: []
# action:
# - service: modbus.write_register
# data_template:
# address: 13050 # 13051
# slave: 1
# # 0xAA: 170 Charge
# # 0xBB: 187 Discharge
# # 0xCC: 204 Stop (Default)
# # don't know how to compare the integer values in hexadecimal format in following decoder statement...
# value: "{% if is_state('input_select.set_sg_battery_charge_discharge_cmd' , 'Stop (default)') %} 204
# {% elif is_state('input_select.set_sg_battery_charge_discharge_cmd' , 'Force charge') %} 170
# {% elif is_state('input_select.set_sg_battery_charge_discharge_cmd' , 'Force discharge') %} 187
# {% else %} 204 {% endif %}"
# hub: SungrowSHx
# mode: single
# - id: "24323313553543" # random number
# alias: update EMS mode
# description: "Updates EMS mode"
# trigger:
# - platform: state
# entity_id:
# - input_select.set_sg_ems_mode
# condition: []
# action:
# - service: modbus.write_register
# data_template:
# address: 13049 # 13050
# slave: 1
# # 0: Self-consumption mode (Default)
# # 1: entry does not exist
# # 2: Forced Mode
# # 3: External EMS
# # 4: VPP
# # don't know how to compare the integer values in hexadecimal format in following decoder statement...
# value: "{% if is_state('input_select.set_sg_ems_mode' , 'Self-consumption mode (default)') %} 0
# {% elif is_state('input_select.set_sg_ems_mode' , 'Forced mode') %} 2
# {% elif is_state('input_select.set_sg_ems_mode' , 'External EMS') %} 3
# {% elif is_state('input_select.set_sg_ems_mode' , 'VPP') %} 4
# {% else %} 0 {% endif %}"
# hub: SungrowSHx
# mode: single
# - id: "24321413543521" # random number
# alias: "update max charge discharge power percent"
# description: "Sets max charge discharge power in % of BDC rated power"
# trigger:
# - platform: state
# entity_id:
# - input_number.set_sg_charge_discharge_power_percentage
# condition: []
# action:
# - service: modbus.write_register
# data_template:
# address: 13051 # 13052
# slave: 1
# value: "{{ states('input_number.set_sg_charge_discharge_power_percentage') | int}}"
# hub: SungrowSHx
# mode: single