mirror of
https://github.com/alexhopeoconnor/SunGather.git
synced 2026-10-04 02:48:12 +10:00
v0.4.0: HomeAssistant Support!
This commit is contained in:
@@ -37,6 +37,7 @@ On first connection the tool will query your inverter, retrieve the model and re
|
|||||||
Register information based on official documentation: <a href="https://github.com/bohdan-s/Sungrow-Inverter/blob/main/Modbus%20Information/Communication%20Protocol%20of%20PV%20Grid-Connected%20String%20Inverters_V1.1.37_EN.pdf">Communication Protocol of PV Grid-Connected String Inverters</a> and <a href="https://github.com/bohdan-s/Sungrow-Inverter/blob/main/Modbus%20Information/communication-protocol-of-residential-hybrid-inverterv1.0.20-1.pdf">Communication Protocol of Residential Hybrid Inverters</a>
|
Register information based on official documentation: <a href="https://github.com/bohdan-s/Sungrow-Inverter/blob/main/Modbus%20Information/Communication%20Protocol%20of%20PV%20Grid-Connected%20String%20Inverters_V1.1.37_EN.pdf">Communication Protocol of PV Grid-Connected String Inverters</a> and <a href="https://github.com/bohdan-s/Sungrow-Inverter/blob/main/Modbus%20Information/communication-protocol-of-residential-hybrid-inverterv1.0.20-1.pdf">Communication Protocol of Residential Hybrid Inverters</a>
|
||||||
|
|
||||||
Has multiple export locations out of the box:
|
Has multiple export locations out of the box:
|
||||||
|
* Full Home Assistant Support, as HassIO add-on
|
||||||
* Console - Log directly to screen
|
* Console - Log directly to screen
|
||||||
* MQTT - Load into MQTT, and optionally Home Assistance including Discovery
|
* MQTT - Load into MQTT, and optionally Home Assistance including Discovery
|
||||||
* PVOutput - Load into PVOutput.org
|
* PVOutput - Load into PVOutput.org
|
||||||
@@ -53,9 +54,6 @@ I have learned a lot from the following projects, THANK YOU
|
|||||||
|
|
||||||
<p align="right">(<a href="#top">back to top</a>)</p>
|
<p align="right">(<a href="#top">back to top</a>)</p>
|
||||||
|
|
||||||
## Roadmap / TO DO
|
|
||||||
* Full Home Assistant integration, as HACS addon
|
|
||||||
|
|
||||||
|
|
||||||
## Built With
|
## Built With
|
||||||
|
|
||||||
@@ -174,6 +172,10 @@ For SG* Models this is calculated from meter_power if -ve value, returned as a p
|
|||||||
|
|
||||||
### Home Assistant setup
|
### Home Assistant setup
|
||||||
|
|
||||||
|
#### The following are manual steps if you are running SunGather separate,
|
||||||
|
#### For the easy way see: <a href="https://github.com/bohdan-s/hassio-repository">Hassio Repository</a></b>
|
||||||
|
|
||||||
|
|
||||||
In the SunGather config.yaml you need to set the smart_meter if you have one
|
In the SunGather config.yaml you need to set the smart_meter if you have one
|
||||||
_For Hybrid Models smart_meter doesn't need to get enabled_
|
_For Hybrid Models smart_meter doesn't need to get enabled_
|
||||||
|
|
||||||
@@ -182,46 +184,20 @@ _For Hybrid Models smart_meter doesn't need to get enabled_
|
|||||||
smart_meter: True
|
smart_meter: True
|
||||||
```
|
```
|
||||||
|
|
||||||
HA sensors created by the MQTT auto-discovery:
|
Enable MQTT, configure the server and HA AutoDiscovery
|
||||||
```
|
```
|
||||||
sensor.inverter_active_power
|
- name: mqtt
|
||||||
sensor.inverter_daily_generation
|
enabled: True
|
||||||
sensor.inverter_export_to_grid
|
host: 192.168.1.200
|
||||||
sensor.inverter_import_from_grid
|
homeassistant: True
|
||||||
sensor.inverter_load_power
|
|
||||||
sensor.inverter_meter_power
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The Inverter reports Power (W), but HA needs Energy (Wh).
|
Electricity Grid > Grid Consumption -> Daily Import from Grid
|
||||||
You can use a "Riemann sum integral" (called integration) to convert it.
|
Electricity Grid > Return to Grid -> Daily Export to Grid
|
||||||
Put the following into your sensors.yaml
|
Solar Panels > Solar Production -> Daily Generation
|
||||||
```
|
|
||||||
sensor:
|
|
||||||
- platform: integration
|
|
||||||
source: sensor.inverter_active_power
|
|
||||||
name: Solar Production (Sungather)
|
|
||||||
- platform: integration
|
|
||||||
source: sensor.inverter_export_to_grid
|
|
||||||
name: Return to Grid (Sungather)
|
|
||||||
- platform: integration
|
|
||||||
source: sensor.inverter_import_from_grid
|
|
||||||
name: Grid Consumption (Sungather)
|
|
||||||
```
|
|
||||||
|
|
||||||
The additional sensors then setup in HA are the following with Wh unit of measurement for the Energy platform.
|
<img src="img/HomeAssistant-SunGrow_Device.png"/>
|
||||||
|
|
||||||
Solar Production (Sungather)
|
|
||||||
Return to Grid (Sungather)
|
|
||||||
Grid Consumption (Sungather)
|
|
||||||
|
|
||||||
Restart Home Assistant for the sensors.yaml to be loaded.
|
|
||||||
Make sure SunGather is running, wait 5 minutes for initial data to populate.
|
|
||||||
Navigate to the Energy platform (Configuration > Energy). Add these sensors to the following areas:
|
|
||||||
|
|
||||||
Electricity Grid > Grid Consumption -> Grid Consumption (Sungather)
|
|
||||||
Electricity Grid > Return to Grid -> Return to Grid (Sungather)
|
|
||||||
Solar Panels > Solar Production -> Solar Production (Sungather)
|
|
||||||
|
|
||||||
<p align="right">(<a href="#top">back to top</a>)</p>
|
<p align="right">(<a href="#top">back to top</a>)</p>
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ inverter:
|
|||||||
# retries: 3 # [Optional] Default is 3, how many times to retry if connection fails
|
# retries: 3 # [Optional] Default is 3, how many times to retry if connection fails
|
||||||
# slave: 0x01 # [Optional] Default is 0x01
|
# slave: 0x01 # [Optional] Default is 0x01
|
||||||
# scan_interval: 30 # [Optional] Default is 30
|
# scan_interval: 30 # [Optional] Default is 30
|
||||||
connection: modbus # [Required] options: modbus, sungrow, http
|
connection: modbus # [Required] options: modbus, sungrow, http
|
||||||
# model: "SG7.0RT" # [Optional] This is autodetected on startup, only needed if detection issues or for testing
|
# model: "SG7.0RT" # [Optional] This is autodetected on startup, only needed if detection issues or for testing
|
||||||
# See model list here: https://github.com/bohdan-s/SunGather#supported
|
# See model list here: https://github.com/bohdan-s/SunGather#supported
|
||||||
# serial: xxxxxxxxxx # [Optional] This is autodetected on startup, only needed if detection issues or for testing, used as a unique ID
|
# serial: xxxxxxxxxx # [Optional] This is autodetected on startup, only needed if detection issues or for testing, used as a unique ID
|
||||||
@@ -24,7 +24,7 @@ inverter:
|
|||||||
exports:
|
exports:
|
||||||
# Print Registers to console, good for debugging / troubleshooting
|
# Print Registers to console, good for debugging / troubleshooting
|
||||||
- name: console
|
- name: console
|
||||||
enabled: False # [Optional] Default is False
|
enabled: True # [Optional] Default is False
|
||||||
|
|
||||||
# Runs a simple Webserver showing Config and last read registers
|
# Runs a simple Webserver showing Config and last read registers
|
||||||
# Access at http://localhost:8080 or http://[serverip]:8080
|
# Access at http://localhost:8080 or http://[serverip]:8080
|
||||||
@@ -38,7 +38,6 @@ exports:
|
|||||||
enabled: False # [Optional] Default is False
|
enabled: False # [Optional] Default is False
|
||||||
# port: 8082 # [Optional] Default is 8080
|
# port: 8082 # [Optional] Default is 8080
|
||||||
|
|
||||||
|
|
||||||
# Output data to InfluxDB
|
# Output data to InfluxDB
|
||||||
- name: influxdb
|
- name: influxdb
|
||||||
enabled: False # [Optional] Default is False
|
enabled: False # [Optional] Default is False
|
||||||
@@ -69,53 +68,60 @@ exports:
|
|||||||
enabled: False # [Optional] Default is False
|
enabled: False # [Optional] Default is False
|
||||||
host: 192.168.1.200 # [Required] IP or Hostname of MQTT Server
|
host: 192.168.1.200 # [Required] IP or Hostname of MQTT Server
|
||||||
# port: 1883 # [Optional] Default 1883
|
# port: 1883 # [Optional] Default 1883
|
||||||
# topic: "inverter/{model}/registers" # [Optional] Default: "inverter/{model}/registers", Variable {model} will be replaced with model number of inverter
|
# topic: "SunGather/{serial_number}" # [Optional] Default: "SunGather/{serial_number}"
|
||||||
# username: # [Optional] Username is MQTT server requires it
|
# username: # [Optional] Username is MQTT server requires it
|
||||||
# password: # [Optional] Password is MQTT server requires it
|
# password: # [Optional] Password is MQTT server requires it
|
||||||
# client_id: # [Optional] Client id for mqtt connection. Defaults to "SunGather", need to be set to different values if you want to run multiple instances of sungather.
|
# client_id: # [Optional] Client id for mqtt connection. Defaults to Serial Number.
|
||||||
homeassistant: False # [Optional] Default False, Publish discovery and sensors for HomeAssistant
|
homeassistant: True
|
||||||
ha_sensors: # [Optional] / [Required] homeassistant: True, Topics to enable discovery for HA
|
ha_sensors:
|
||||||
- name: "Daily Generation"
|
- name: "Daily Generation"
|
||||||
sensor_type: sensor
|
sensor_type: sensor
|
||||||
register: daily_power_yields
|
register: daily_power_yields
|
||||||
dev_class: energy
|
device_class: energy
|
||||||
state_class: total_increasing
|
state_class: total_increasing
|
||||||
- name: "Active Power"
|
icon: "mdi:solar-power"
|
||||||
|
- name: "Daily Export to Grid"
|
||||||
sensor_type: sensor
|
sensor_type: sensor
|
||||||
register: total_active_power
|
register: daily_export_to_grid
|
||||||
dev_class: power
|
device_class: energy
|
||||||
state_class: measurement
|
state_class: total_increasing
|
||||||
- name: "Load Power"
|
icon: "mdi:transmission-tower-export"
|
||||||
|
value_template: "{{ value_json.daily_export_to_grid | round(2) }}"
|
||||||
|
last_reset_value_template: "{{ value_json.last_reset }}"
|
||||||
|
- name: "Daily Import from Grid"
|
||||||
sensor_type: sensor
|
sensor_type: sensor
|
||||||
register: load_power
|
register: daily_import_from_grid
|
||||||
dev_class: power
|
device_class: energy
|
||||||
state_class: measurement
|
state_class: total_increasing
|
||||||
- name: "Meter Power"
|
icon: "mdi:transmission-tower-import"
|
||||||
sensor_type: sensor
|
value_template: "{{ value_json.daily_import_from_grid | round(2) }}"
|
||||||
register: meter_power
|
last_reset_value_template: "{{ value_json.last_reset }}"
|
||||||
dev_class: power
|
|
||||||
state_class: measurement
|
|
||||||
- name: "Export to Grid"
|
|
||||||
sensor_type: sensor
|
|
||||||
register: export_to_grid
|
|
||||||
dev_class: power
|
|
||||||
state_class: measurement
|
|
||||||
- name: "Import from Grid"
|
|
||||||
sensor_type: sensor
|
|
||||||
register: import_from_grid
|
|
||||||
dev_class: power
|
|
||||||
state_class: measurement
|
|
||||||
- name: "Temperature"
|
- name: "Temperature"
|
||||||
sensor_type: sensor
|
sensor_type: sensor
|
||||||
register: internal_temperature
|
register: internal_temperature
|
||||||
dev_class: temperature
|
device_class: temperature
|
||||||
state_class: measurement
|
state_class: measurement
|
||||||
- name: "Power State"
|
- name: "Power State"
|
||||||
sensor_type: binary_sensor
|
sensor_type: binary_sensor
|
||||||
register: run_state
|
register: run_state
|
||||||
dev_class: running
|
device_class: running
|
||||||
payload_on: "ON"
|
payload_on: "ON"
|
||||||
payload_off: "OFF"
|
payload_off: "OFF"
|
||||||
|
- name: "Active Generation"
|
||||||
|
sensor_type: sensor
|
||||||
|
register: total_active_power
|
||||||
|
dev_class: power
|
||||||
|
state_class: measurement
|
||||||
|
- name: "Active Export to Grid"
|
||||||
|
sensor_type: sensor
|
||||||
|
register: export_to_grid
|
||||||
|
dev_class: power
|
||||||
|
state_class: measurement
|
||||||
|
- name: "Active Import from Grid"
|
||||||
|
sensor_type: sensor
|
||||||
|
register: import_from_grid
|
||||||
|
dev_class: power
|
||||||
|
state_class: measurement
|
||||||
|
|
||||||
# Publish Registers to PVOutput
|
# Publish Registers to PVOutput
|
||||||
- name: pvoutput
|
- name: pvoutput
|
||||||
|
|||||||
+39
-25
File diff suppressed because one or more lines are too long
@@ -25,7 +25,10 @@ class export_webserver(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def publish(self, inverter):
|
def publish(self, inverter):
|
||||||
body = "<h3>Sungather v" + __version__ + "</h3></p><table><th>Address</th><tr><th>Register</th><th>Value</th></tr>"
|
body = "<h3>SunGather v" + __version__ + "</h3></p>"
|
||||||
|
body += "<h4>Need Help? <href a='https://github.com/bohdan-s/SunGather'>https://github.com/bohdan-s/SunGather</a></h4></p>"
|
||||||
|
body += "<h4>NEW HomeAssistant Add-on: <href a='https://github.com/bohdan-s/hassio-repository'>https://github.com/bohdan-s/SunGather</a></h4></p>"
|
||||||
|
body += "<table><th>Address</th><tr><th>Register</th><th>Value</th></tr>"
|
||||||
metrics = ""
|
metrics = ""
|
||||||
for register, value in inverter.latest_scrape.items():
|
for register, value in inverter.latest_scrape.items():
|
||||||
body += f"<tr><td>{str(inverter.getRegisterAddress(register))}</td><td>{str(register)}</td><td>{str(value)} {str(inverter.getRegisterUnit(register))}</td></tr>"
|
body += f"<tr><td>{str(inverter.getRegisterAddress(register))}</td><td>{str(register)}</td><td>{str(value)} {str(inverter.getRegisterUnit(register))}</td></tr>"
|
||||||
@@ -53,7 +56,10 @@ class MyServer(BaseHTTPRequestHandler):
|
|||||||
self.send_response(200)
|
self.send_response(200)
|
||||||
self.send_header("Content-type", "text/html")
|
self.send_header("Content-type", "text/html")
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
self.wfile.write(bytes("<html><head><title>SunGather</title><meta charset='UTF-8'><meta http-equiv='refresh' content='15'></head>", "utf-8"))
|
self.wfile.write(bytes("<html><head><title>SunGather</title>", "utf-8"))
|
||||||
|
self.wfile.write(bytes("<meta charset='UTF-8'><meta http-equiv='refresh' content='15'>", "utf-8"))
|
||||||
|
self.wfile.write(bytes('<style media = "all"> body { background-color: black; color: white; } @media screen and (prefers-color-scheme: light) { body { background-color: white; color: black; } } </style>', "utf-8"))
|
||||||
|
self.wfile.write(bytes("</head>", "utf-8"))
|
||||||
self.wfile.write(bytes("<body>", "utf-8"))
|
self.wfile.write(bytes("<body>", "utf-8"))
|
||||||
self.wfile.write(bytes(export_webserver.html_body, "utf-8"))
|
self.wfile.write(bytes(export_webserver.html_body, "utf-8"))
|
||||||
self.wfile.write(bytes("</table>", "utf-8"))
|
self.wfile.write(bytes("</table>", "utf-8"))
|
||||||
|
|||||||
+108
-55
@@ -25,19 +25,29 @@ class SungrowInverter():
|
|||||||
"RetryOnEmpty": False,
|
"RetryOnEmpty": False,
|
||||||
}
|
}
|
||||||
self.inverter_config = {
|
self.inverter_config = {
|
||||||
"slave": config_inverter.get('slave'),
|
|
||||||
"model": config_inverter.get('model'),
|
"model": config_inverter.get('model'),
|
||||||
"serial": config_inverter.get('serial'),
|
"serial_number": config_inverter.get('serial_number'),
|
||||||
"level": config_inverter.get('level'),
|
"level": config_inverter.get('level'),
|
||||||
|
"scan_interval": config_inverter.get('scan_interval'),
|
||||||
"use_local_time": config_inverter.get('use_local_time'),
|
"use_local_time": config_inverter.get('use_local_time'),
|
||||||
"smart_meter": config_inverter.get('smart_meter'),
|
"smart_meter": config_inverter.get('smart_meter'),
|
||||||
"connection": config_inverter.get('connection')
|
"connection": config_inverter.get('connection'),
|
||||||
|
"slave": config_inverter.get('slave'),
|
||||||
|
"start_time": ""
|
||||||
}
|
}
|
||||||
self.client = None
|
self.client = None
|
||||||
|
|
||||||
self.registers = [[]]
|
self.registers = [[]]
|
||||||
self.registers.pop() # Remove null value from list
|
self.registers.pop() # Remove null value from list
|
||||||
self.registers_custom = [{'name': 'export_to_grid', 'unit': 'W', 'address': 'vr001'}, {'name': 'import_from_grid', 'unit': 'W', 'address': 'vr002'}, {'name': 'run_state', 'address': 'vr003'}, {'name': 'timestamp', 'address': 'vr004'}]
|
self.registers_custom = [ {'name': 'run_state', 'address': 'vr001'},
|
||||||
|
{'name': 'timestamp', 'address': 'vr002'},
|
||||||
|
{'name': 'last_reset', 'address': 'vr003'},
|
||||||
|
{'name': 'export_to_grid', 'unit': 'W', 'address': 'vr004'},
|
||||||
|
{'name': 'import_from_grid', 'unit': 'W', 'address': 'vr005'},
|
||||||
|
{'name': 'daily_export_to_grid', 'unit': 'kWh', 'address': 'vr006'},
|
||||||
|
{'name': 'daily_import_from_grid', 'unit': 'kWh', 'address': 'vr007'}
|
||||||
|
]
|
||||||
|
|
||||||
self.register_ranges = [[]]
|
self.register_ranges = [[]]
|
||||||
self.register_ranges.pop() # Remove null value from list
|
self.register_ranges.pop() # Remove null value from list
|
||||||
|
|
||||||
@@ -330,12 +340,19 @@ class SungrowInverter():
|
|||||||
def scrape(self):
|
def scrape(self):
|
||||||
scrape_start = datetime.now()
|
scrape_start = datetime.now()
|
||||||
|
|
||||||
# Clear previous inverter values, keep the model and run state
|
# Clear previous inverter values, persist some values
|
||||||
if self.latest_scrape.get("run_state"): run_state = self.latest_scrape.get("run_state")
|
persist_registers = {
|
||||||
else: run_state = "ON"
|
"run_state": self.latest_scrape.get("run_state","ON"),
|
||||||
|
"last_reset": self.latest_scrape.get("last_reset",""),
|
||||||
|
"daily_export_to_grid": self.latest_scrape.get("daily_export_to_grid",0),
|
||||||
|
"daily_import_from_grid": self.latest_scrape.get("daily_import_from_grid",0),
|
||||||
|
}
|
||||||
|
|
||||||
self.latest_scrape = {}
|
self.latest_scrape = {}
|
||||||
self.latest_scrape['device_type_code'] = self.inverter_config['model']
|
self.latest_scrape['device_type_code'] = self.inverter_config['model']
|
||||||
self.latest_scrape["run_state"] = run_state
|
|
||||||
|
for register, value in persist_registers.items():
|
||||||
|
self.latest_scrape[register] = value
|
||||||
|
|
||||||
load_registers_count = 0
|
load_registers_count = 0
|
||||||
load_registers_failed = 0
|
load_registers_failed = 0
|
||||||
@@ -347,7 +364,7 @@ class SungrowInverter():
|
|||||||
load_registers_failed +=1
|
load_registers_failed +=1
|
||||||
if load_registers_failed == load_registers_count:
|
if load_registers_failed == load_registers_count:
|
||||||
# If every scrape fails, disconnect the client
|
# If every scrape fails, disconnect the client
|
||||||
logging.warning
|
#logging.warning
|
||||||
self.disconnect()
|
self.disconnect()
|
||||||
return False
|
return False
|
||||||
if load_registers_failed > 0:
|
if load_registers_failed > 0:
|
||||||
@@ -356,52 +373,7 @@ class SungrowInverter():
|
|||||||
# Leave connection open, see if helps resolve the connection issues
|
# Leave connection open, see if helps resolve the connection issues
|
||||||
#self.close()
|
#self.close()
|
||||||
|
|
||||||
# Create a registers for Power imported and exported to/from Grid
|
## vr002
|
||||||
if self.inverter_config['level'] >= 1:
|
|
||||||
self.latest_scrape["export_to_grid"] = 0
|
|
||||||
self.latest_scrape["import_from_grid"] = 0
|
|
||||||
|
|
||||||
if self.validateRegister('meter_power'):
|
|
||||||
try:
|
|
||||||
power = self.latest_scrape.get('meter_power', self.latest_scrape.get('export_power', 0))
|
|
||||||
if power < 0:
|
|
||||||
self.latest_scrape["export_to_grid"] = abs(power)
|
|
||||||
elif power >= 0:
|
|
||||||
self.latest_scrape["import_from_grid"] = power
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
# in this case we connected to a hybrid inverter and need to use export_power_hybrid
|
|
||||||
# export_power_hybrid is negative in case of importing from the grid
|
|
||||||
elif self.validateRegister('export_power_hybrid'):
|
|
||||||
try:
|
|
||||||
power = self.latest_scrape.get('export_power_hybrid', 0)
|
|
||||||
if power < 0:
|
|
||||||
self.latest_scrape["import_from_grid"] = abs(power)
|
|
||||||
elif power >= 0:
|
|
||||||
self.latest_scrape["export_to_grid"] = power
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
try: # If inverter is returning no data for load_power, we can calculate it manually
|
|
||||||
if not self.latest_scrape["load_power"]:
|
|
||||||
self.latest_scrape["load_power"] = int(self.latest_scrape.get('total_active_power')) + int(self.latest_scrape.get('meter_power'))
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# See if the inverter is running, This is added to inverters so can be read via MQTT etc...
|
|
||||||
# It is also used below, as some registers hold the last value on 'stop' so we need to set to 0
|
|
||||||
# to help with graphing.
|
|
||||||
try:
|
|
||||||
if self.latest_scrape.get('start_stop'):
|
|
||||||
if self.latest_scrape.get('start_stop', False) == 'Start' and self.latest_scrape.get('work_state_1', False).contains('Run'):
|
|
||||||
self.latest_scrape["run_state"] = "ON"
|
|
||||||
else:
|
|
||||||
self.latest_scrape["run_state"] = "OFF"
|
|
||||||
else:
|
|
||||||
self.latest_scrape["run_state"] = "OFF"
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if self.inverter_config.get('use_local_time',False):
|
if self.inverter_config.get('use_local_time',False):
|
||||||
self.latest_scrape["timestamp"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
self.latest_scrape["timestamp"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
logging.debug(f'Using Local Computer Time: {self.latest_scrape.get("timestamp")}')
|
logging.debug(f'Using Local Computer Time: {self.latest_scrape.get("timestamp")}')
|
||||||
@@ -451,6 +423,85 @@ class SungrowInverter():
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
### Custom Registers
|
||||||
|
######################
|
||||||
|
|
||||||
|
## vr001 - run_state
|
||||||
|
# See if the inverter is running, This is added to inverters so can be read via MQTT etc...
|
||||||
|
# It is also used below, as some registers hold the last value on 'stop' so we need to set to 0
|
||||||
|
# to help with graphing.
|
||||||
|
try:
|
||||||
|
if self.latest_scrape.get('start_stop'):
|
||||||
|
logging.info(f"DEBUG: start_stop:{self.latest_scrape.get('start_stop', 'null')} work_state_1:{self.latest_scrape.get('work_state_1', 'null')}")
|
||||||
|
if self.latest_scrape.get('start_stop', False) == 'Start' and self.latest_scrape.get('work_state_1', False).contains('Run'):
|
||||||
|
self.latest_scrape["run_state"] = "ON"
|
||||||
|
else:
|
||||||
|
self.latest_scrape["run_state"] = "OFF"
|
||||||
|
else:
|
||||||
|
logging.info(f"DEBUG: Couldn't read start_stop so run_state is OFF")
|
||||||
|
self.latest_scrape["run_state"] = "OFF"
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
## vr003 - last_reset
|
||||||
|
date_format = "%Y-%m-%d %H:%M:%S"
|
||||||
|
if not self.latest_scrape.get('last_reset', False):
|
||||||
|
logging.info('Setting Initial Daily registers; daily_export_to_grid, daily_import_from_grid, last_reset')
|
||||||
|
self.latest_scrape["daily_export_to_grid"] = 0
|
||||||
|
self.latest_scrape["daily_import_from_grid"] = 0
|
||||||
|
self.latest_scrape['last_reset'] = self.latest_scrape["timestamp"]
|
||||||
|
elif datetime.strptime(self.latest_scrape['last_reset'], date_format).date() < datetime.strptime(self.latest_scrape['timestamp'], date_format).date():
|
||||||
|
logging.info('last_reset: ' + self.latest_scrape['last_reset'] + ', timestamp: ' + self.latest_scrape['timestamp'])
|
||||||
|
logging.info('Resetting Daily registers; daily_export_to_grid, daily_import_from_grid, last_reset')
|
||||||
|
self.latest_scrape["daily_export_to_grid"] = 0
|
||||||
|
self.latest_scrape["daily_import_from_grid"] = 0
|
||||||
|
self.latest_scrape['last_reset'] = self.latest_scrape["timestamp"]
|
||||||
|
|
||||||
|
## vr004 - import_from_grid, vr005 - export_to_grid
|
||||||
|
# Create a registers for Power imported and exported to/from Grid
|
||||||
|
if self.inverter_config['level'] >= 1:
|
||||||
|
self.latest_scrape["export_to_grid"] = 0
|
||||||
|
self.latest_scrape["import_from_grid"] = 0
|
||||||
|
|
||||||
|
if self.validateRegister('meter_power'):
|
||||||
|
try:
|
||||||
|
power = self.latest_scrape.get('meter_power', self.latest_scrape.get('export_power', 0))
|
||||||
|
if power < 0:
|
||||||
|
self.latest_scrape["export_to_grid"] = abs(power)
|
||||||
|
elif power >= 0:
|
||||||
|
self.latest_scrape["import_from_grid"] = power
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
# in this case we connected to a hybrid inverter and need to use export_power_hybrid
|
||||||
|
# export_power_hybrid is negative in case of importing from the grid
|
||||||
|
elif self.validateRegister('export_power_hybrid'):
|
||||||
|
try:
|
||||||
|
power = self.latest_scrape.get('export_power_hybrid', 0)
|
||||||
|
if power < 0:
|
||||||
|
self.latest_scrape["import_from_grid"] = abs(power)
|
||||||
|
elif power >= 0:
|
||||||
|
self.latest_scrape["export_to_grid"] = power
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try: # If inverter is returning no data for load_power, we can calculate it manually
|
||||||
|
if not self.latest_scrape["load_power"]:
|
||||||
|
self.latest_scrape["load_power"] = int(self.latest_scrape.get('total_active_power')) + int(self.latest_scrape.get('meter_power'))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
## vr004, vr005
|
||||||
|
if not self.latest_scrape.get('daily_export_to_grid', False):
|
||||||
|
self.latest_scrape["daily_export_to_grid"] = 0
|
||||||
|
|
||||||
|
self.latest_scrape["daily_export_to_grid"] += ((self.latest_scrape["export_to_grid"] / 1000) * (self.inverter_config['scan_interval'] / 60 / 60) )
|
||||||
|
|
||||||
|
## vr005
|
||||||
|
if not self.latest_scrape.get('daily_import_from_grid', False):
|
||||||
|
self.latest_scrape["daily_import_from_grid"] = 0
|
||||||
|
|
||||||
|
self.latest_scrape["daily_import_from_grid"] += ((self.latest_scrape["import_from_grid"] / 1000) * (self.inverter_config['scan_interval'] / 60 / 60) )
|
||||||
|
|
||||||
scrape_end = datetime.now()
|
scrape_end = datetime.now()
|
||||||
logging.info(f'Inverter: Successfully scraped in {(scrape_end - scrape_start).seconds}.{(scrape_end - scrape_start).microseconds} secs')
|
logging.info(f'Inverter: Successfully scraped in {(scrape_end - scrape_start).seconds}.{(scrape_end - scrape_start).microseconds} secs')
|
||||||
|
|
||||||
@@ -502,6 +553,8 @@ def main():
|
|||||||
runonce = True
|
runonce = True
|
||||||
|
|
||||||
logging.info(f'Starting SunGather {__version__}')
|
logging.info(f'Starting SunGather {__version__}')
|
||||||
|
logging.info(f'Need Help? https://github.com/bohdan-s/SunGather')
|
||||||
|
logging.info(f'NEW HomeAssistant Add-on: https://github.com/bohdan-s/hassio-repository')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
configfile = yaml.safe_load(open(configfilename, encoding="utf-8"))
|
configfile = yaml.safe_load(open(configfilename, encoding="utf-8"))
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__ = '0.3.9'
|
__version__ = '0.4.0'
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 354 KiB |
Reference in New Issue
Block a user