Update Readme to be clearer about HA

This commit is contained in:
bohdan-s
2022-02-02 14:34:30 +11:00
parent 1a2ca4397c
commit 9c96860012
4 changed files with 72 additions and 71 deletions
+3 -1
View File
@@ -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:
+62 -63
View File
@@ -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()
+6 -6
View File
@@ -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
+1 -1
View File
@@ -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