mirror of
https://github.com/alexhopeoconnor/SunGather.git
synced 2026-10-19 01:11:30 +10:00
Update Readme to be clearer about HA
This commit is contained in:
@@ -30,7 +30,7 @@ Access ModBus data from almost any network connected Sungow Inverter.
|
||||
|
||||
On first connection the tool will query your inverter, retrieve the model and return the correct registers for your device. No more searching registers or creating model files.
|
||||
|
||||
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 hreh="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:
|
||||
* Console - Log directly to screen
|
||||
@@ -219,6 +219,8 @@ sensor.inverter_meter_power
|
||||
|
||||
```
|
||||
|
||||
The Inverter reports Power (W), but HA needs Energy (Wh).
|
||||
You can use a "Riemann sum integral" (called integration) to convert it.
|
||||
Put the following into your sensors.yaml
|
||||
```
|
||||
sensor:
|
||||
|
||||
@@ -143,73 +143,72 @@ class export_pvoutput(object):
|
||||
return True
|
||||
|
||||
def publish(self, inverter):
|
||||
self.collect_data(inverter)
|
||||
|
||||
# Process data points every status_interval
|
||||
if((time.time() - self.last_publish) >= (self.status_interval * 60)):
|
||||
any_data = False
|
||||
if inverter.validateLatestScrape('timestamp'):
|
||||
now = datetime.datetime.strptime(inverter.getRegisterValue('timestamp'), "%Y-%m-%d %H:%M:%S")
|
||||
data_point = str(now.strftime("%Y%m%d")) + "," + str(now.strftime("%H:%M"))
|
||||
for x in range(1, 13):
|
||||
field = 'v' + str(x)
|
||||
if self.collected_data.get(field):
|
||||
if x == 6 or x == 7: # Round to 1 decimal place
|
||||
value = round(self.collected_data[field] / self.collected_data['count'], 1)
|
||||
else: # Getting errors when uploading decimals for power/energy so return INT
|
||||
value = int((self.collected_data[field] / self.collected_data['count']))
|
||||
data_point = data_point + "," + str(value)
|
||||
any_data = True
|
||||
else:
|
||||
data_point = data_point + ","
|
||||
self.collected_data = {}
|
||||
|
||||
if any_data:
|
||||
self.batch_data.append(data_point)
|
||||
else:
|
||||
logging.warning(f"PVOutput: No data collected in last {(self.status_interval * 60)} minutes")
|
||||
|
||||
# Max upload is 30, if over 30 then remove the oldest one
|
||||
if self.batch_data.__len__() > 30:
|
||||
logging.warning(f"PVOutput: Over 30 data points scheduled to upload. max is 30 so removing oldest data point")
|
||||
self.batch_data.pop(0)
|
||||
|
||||
self.batch_count +=1
|
||||
if self.batch_count >= self.pvoutput_config['batch_points']:
|
||||
if not self.batch_data.__len__() > 0:
|
||||
logging.warning(f"PVOutput: No data collected in last {((self.status_interval * 60) * self.batch_count)} minutes, Skipping upload")
|
||||
return False
|
||||
elif self.batch_data.__len__() >= 1:
|
||||
payload_data = None
|
||||
for data in self.batch_data:
|
||||
if payload_data:
|
||||
payload_data = payload_data + ";" + data
|
||||
if self.collect_data(inverter):
|
||||
# Process data points every status_interval
|
||||
if((time.time() - self.last_publish) >= (self.status_interval * 60)):
|
||||
any_data = False
|
||||
if inverter.validateLatestScrape('timestamp'):
|
||||
now = datetime.datetime.strptime(inverter.getRegisterValue('timestamp'), "%Y-%m-%d %H:%M:%S")
|
||||
data_point = str(now.strftime("%Y%m%d")) + "," + str(now.strftime("%H:%M"))
|
||||
for x in range(1, 13):
|
||||
field = 'v' + str(x)
|
||||
if self.collected_data.get(field):
|
||||
if x == 6 or x == 7: # Round to 1 decimal place
|
||||
value = round(self.collected_data[field] / self.collected_data['count'], 1)
|
||||
else: # Getting errors when uploading decimals for power/energy so return INT
|
||||
value = int((self.collected_data[field] / self.collected_data['count']))
|
||||
data_point = data_point + "," + str(value)
|
||||
any_data = True
|
||||
else:
|
||||
payload_data = data
|
||||
data_point = data_point + ","
|
||||
self.collected_data = {}
|
||||
|
||||
payload = {}
|
||||
payload['data'] = payload_data
|
||||
if any_data:
|
||||
self.batch_data.append(data_point)
|
||||
else:
|
||||
logging.warning(f"PVOutput: No data collected in last {(self.status_interval * 60)} minutes")
|
||||
|
||||
if self.pvoutput_config['cumulative_flag'] > 0:
|
||||
payload['c1'] = self.pvoutput_config['cumulative_flag']
|
||||
# Max upload is 30, if over 30 then remove the oldest one
|
||||
if self.batch_data.__len__() > 30:
|
||||
logging.warning(f"PVOutput: Over 30 data points scheduled to upload. max is 30 so removing oldest data point")
|
||||
self.batch_data.pop(0)
|
||||
|
||||
try:
|
||||
logging.debug("PVOutput: Request; " + self.url_addbatchstatus + ", " + str(self.headers) + " : " + str(payload))
|
||||
response = requests.post(url=self.url_addbatchstatus, headers=self.headers, params=payload, timeout=3)
|
||||
self.batch_count = 0
|
||||
self.batch_count +=1
|
||||
if self.batch_count >= self.pvoutput_config['batch_points']:
|
||||
if not self.batch_data.__len__() > 0:
|
||||
logging.warning(f"PVOutput: No data collected in last {((self.status_interval * 60) * self.batch_count)} minutes, Skipping upload")
|
||||
return False
|
||||
elif self.batch_data.__len__() >= 1:
|
||||
payload_data = None
|
||||
for data in self.batch_data:
|
||||
if payload_data:
|
||||
payload_data = payload_data + ";" + data
|
||||
else:
|
||||
payload_data = data
|
||||
|
||||
if response.status_code != requests.codes.ok:
|
||||
logging.error(f"PVOutput: Upload Failed; {str(response.status_code)} Message; {str(response.text)}")
|
||||
else:
|
||||
self.batch_data = []
|
||||
self.last_publish = time.time()
|
||||
logging.info("PVOutput: Data uploaded")
|
||||
except Exception as err:
|
||||
logging.error(f"PVOutput: Failed to Upload")
|
||||
logging.debug(f"{err}")
|
||||
payload = {}
|
||||
payload['data'] = payload_data
|
||||
|
||||
if self.pvoutput_config['cumulative_flag'] > 0:
|
||||
payload['c1'] = self.pvoutput_config['cumulative_flag']
|
||||
|
||||
try:
|
||||
logging.debug("PVOutput: Request; " + self.url_addbatchstatus + ", " + str(self.headers) + " : " + str(payload))
|
||||
response = requests.post(url=self.url_addbatchstatus, headers=self.headers, params=payload, timeout=3)
|
||||
self.batch_count = 0
|
||||
|
||||
if response.status_code != requests.codes.ok:
|
||||
logging.error(f"PVOutput: Upload Failed; {str(response.status_code)} Message; {str(response.text)}")
|
||||
else:
|
||||
self.batch_data = []
|
||||
self.last_publish = time.time()
|
||||
logging.info("PVOutput: Data uploaded")
|
||||
except Exception as err:
|
||||
logging.error(f"PVOutput: Failed to Upload")
|
||||
logging.debug(f"{err}")
|
||||
else:
|
||||
logging.info("PVOutput: Data added to next batch upload")
|
||||
else:
|
||||
logging.info("PVOutput: Data added to next batch upload")
|
||||
else:
|
||||
logging.info(f"PVOutput: Data logged, next upload in {int(((self.status_interval) * 60) - (time.time() - self.last_publish))} secs")
|
||||
logging.info(f"PVOutput: Data logged, next upload in {int(((self.status_interval) * 60) - (time.time() - self.last_publish))} secs")
|
||||
|
||||
self.last_run = time.time()
|
||||
self.last_run = time.time()
|
||||
@@ -432,7 +432,7 @@ registers:
|
||||
level: 2
|
||||
address: 5077
|
||||
datatype: "U32"
|
||||
unit: "w"
|
||||
unit: "W"
|
||||
models: ["SG30KTL", "SG10KTL", "SG12KTL", "SG15KTL", "SG20KTL", "SG30KU", "SG36KTL", "SG36KU", "SG40KTL", "SG40KTL-M", "SG50KTL-M", "SG60KTL-M", "SG60KU", "SG30KTL-M", "SG30KTL-M-V31", "SG33KTL-M", "SG36KTL-M", "SG33K3J", "SG49K5J", "SG34KJ", "LP_P34KSG", "SG50KTL-M-20", "SG60KTL", "SG80KTL", "SG80KTL-20", "SG60KU-M", "SG5KTL-MT", "SG6KTL-MT", "SG8KTL-M", "SG10KTL-M", "SG10KTL-MT", "SG12KTL-M", "SG15KTL-M", "SG17KTL-M", "SG20KTL-M", "SG80KTL-M", "SG111HV", "SG125HV", "SG125HV-20", "SG30CX", "SG33CX", "SG36CX-US", "SG40CX", "SG50CX", "SG60CX-US", "SG110CX", "SG250HX", "SG250HX-US", "SG100CX", "SG100CX-JP", "SG250HX-IN", "SG25CX-SA", "SG75CX", "SG3.0RT", "SG4.0RT", "SG5.0RT", "SG6.0RT", "SG7.0RT", "SG8.0RT", "SG10RT", "SG12RT", "SG15RT", "SG17RT", "SG20RT"]
|
||||
- name: "reactive_power_regulation_setpoint"
|
||||
level: 2
|
||||
@@ -480,32 +480,32 @@ registers:
|
||||
datatype: "S32"
|
||||
indicator: True
|
||||
smart_meter: True
|
||||
unit: "w"
|
||||
unit: "W"
|
||||
models: ["SG5KTL-MT","SG6KTL-MT","SG8KTL-M","SG10KTL-M","SG10KTL-MT","SG12KTL-M","SG15KTL-M","SG17KTL-M","SG20KTL-M"]
|
||||
- name: "meter_a_phase_power"
|
||||
level: 2
|
||||
address: 5085
|
||||
datatype: "S32"
|
||||
unit: "w"
|
||||
unit: "W"
|
||||
models: ["SG5KTL-MT","SG6KTL-MT","SG8KTL-M","SG10KTL-M","SG10KTL-MT","SG12KTL-M","SG15KTL-M","SG17KTL-M","SG20KTL-M"]
|
||||
- name: "meter_b_phase_power"
|
||||
level: 2
|
||||
address: 5087
|
||||
datatype: "S32"
|
||||
unit: "w"
|
||||
unit: "W"
|
||||
models: ["SG5KTL-MT","SG6KTL-MT","SG8KTL-M","SG10KTL-M","SG10KTL-MT","SG12KTL-M","SG15KTL-M","SG17KTL-M","SG20KTL-M"]
|
||||
- name: "meter_c_phase_power"
|
||||
level: 2
|
||||
address: 5089
|
||||
datatype: "S32"
|
||||
unit: "w"
|
||||
unit: "W"
|
||||
models: ["SG5KTL-MT","SG6KTL-MT","SG8KTL-M","SG10KTL-M","SG10KTL-MT","SG12KTL-M","SG15KTL-M","SG17KTL-M","SG20KTL-M"]
|
||||
- name: "load_power"
|
||||
level: 1
|
||||
address: 5091
|
||||
datatype: "S32"
|
||||
smart_meter: True
|
||||
unit: "w"
|
||||
unit: "W"
|
||||
models: ["SG5KTL-MT","SG6KTL-MT","SG8KTL-M","SG10KTL-M","SG10KTL-MT","SG12KTL-M","SG15KTL-M","SG17KTL-M","SG20KTL-M"]
|
||||
- name: "daily_export_energy"
|
||||
level: 1
|
||||
|
||||
@@ -35,7 +35,7 @@ class SungrowInverter():
|
||||
|
||||
self.registers = [[]]
|
||||
self.registers.pop() # Remove null value from list
|
||||
self.registers_custom = [{'name': 'export_to_grid', 'unit': 'w'}, {'name': 'import_from_grid', 'unit': 'w'}, {'name': 'run_state'}]
|
||||
self.registers_custom = [{'name': 'export_to_grid', 'unit': 'W'}, {'name': 'import_from_grid', 'unit': 'W'}, {'name': 'run_state'}]
|
||||
self.register_ranges = [[]]
|
||||
self.register_ranges.pop() # Remove null value from list
|
||||
|
||||
|
||||
Reference in New Issue
Block a user