diff --git a/.DS_Store b/.DS_Store index e37503d..8881bdd 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/debug.py b/debug.py index 22708ff..31e14dd 100755 --- a/debug.py +++ b/debug.py @@ -1,5 +1,7 @@ #!/usr/bin/env python3 -from solaxx3.rs485 import SolaxX3 +#from solaxx3.rs485 import SolaxX3 +from solaxx3.solaxx3 import SolaxX3 + solax_items=[ "power_dc1", "power_dc2", @@ -88,7 +90,7 @@ if s.connect(): echarge_today = s.read("echarge_today")[0] energy_from_grid = s.read("energy_from_grid_meter")[0] energy_to_grid = s.read("energy_to_grid_meter")[0] - power_to_ev = s.read("power_to_ev")[0] +# power_to_ev = s.read("power_to_ev")[0] feed_in_power = s.read("feed_in_power")[0] output_energy_charge = s.read("output_energy_charge")[0] output_energy_today = s.read("output_energy_charge_today")[0] diff --git a/solax.py b/solax.py index 446e4fa..4bdc49b 100755 --- a/solax.py +++ b/solax.py @@ -1,4 +1,6 @@ -from solaxx3.rs485 import SolaxX3 +#from solaxx3.rs485 import SolaxX3 +from solaxx3.solaxx3 import SolaxX3 + from influxdb_client import InfluxDBClient, Point, WritePrecision from influxdb_client.client.write_api import SYNCHRONOUS token = "Wt0uDqp1gLr7Qe6qoUAohD8ciBva0kCRajHEFtRGDhSY5-TI2ASPJmqlQTUrcK-61rUAVedwiiLu5Iy-G41ByQ==" @@ -44,8 +46,9 @@ solax_items=[ "grid_voltage_r_meter", "grid_voltage_s_meter", "grid_voltage_t_meter", -"firmware_version_dsp", -"firmware_version_arm" +"firmware_version_dsp_minor", +"firmware_version_arm_minor", +"work_mode" ] @@ -83,7 +86,8 @@ openhab_items=[ "Solax_GridVoltage_2_meter", "Solax_GridVoltage_3_meter", "Solax_FW_DSP", -"Solax_FW_ARM" +"Solax_FW_ARM", +"Solax_WorkMode" ] # adjust the serial port and baud rate as necessary @@ -109,7 +113,7 @@ if s.connect(): echarge_today = s.read("echarge_today")[0] energy_from_grid = s.read("energy_from_grid_meter")[0] energy_to_grid = s.read("energy_to_grid_meter")[0] - power_to_ev = s.read("power_to_ev")[0] + #power_to_ev = s.read("power_to_ev")[0] feed_in_power = s.read("feed_in_power")[0] output_energy_charge = s.read("output_energy_charge")[0] output_energy_today = s.read("output_energy_charge_today")[0] diff --git a/source/.DS_Store b/source/.DS_Store index 9a874b5..58f9707 100644 Binary files a/source/.DS_Store and b/source/.DS_Store differ diff --git a/source/README.md b/source/README.md index 1732c15..c8c5941 100644 --- a/source/README.md +++ b/source/README.md @@ -1,3 +1,7 @@ +![Build badge](https://github.com/mkfam7/solaxx3/actions/workflows/python-package.yml/badge.svg) + + + # solax-x3 #### Read in real-time all parameters provided by Solax X3 solar inverter via its Modbus S-485 serial interface. @@ -21,7 +25,7 @@ pip install solaxx3 ``` -from solaxx3.rs485 import SolaxX3 +from solaxx3.solaxx3 import SolaxX3 # adjust the serial port and baud rate as necessary s = SolaxX3(port="/dev/ttyUSB0", baudrate=115200) diff --git a/source/UNKNOWN.egg-info/PKG-INFO b/source/UNKNOWN.egg-info/PKG-INFO deleted file mode 100644 index a89f0fc..0000000 --- a/source/UNKNOWN.egg-info/PKG-INFO +++ /dev/null @@ -1,11 +0,0 @@ -Metadata-Version: 2.1 -Name: UNKNOWN -Version: 0.0.0 -Summary: UNKNOWN -Home-page: UNKNOWN -License: UNKNOWN -Platform: UNKNOWN -License-File: LICENSE - -UNKNOWN - diff --git a/source/UNKNOWN.egg-info/SOURCES.txt b/source/UNKNOWN.egg-info/SOURCES.txt deleted file mode 100644 index bc91d28..0000000 --- a/source/UNKNOWN.egg-info/SOURCES.txt +++ /dev/null @@ -1,8 +0,0 @@ -LICENSE -README.md -pyproject.toml -setup.py -UNKNOWN.egg-info/PKG-INFO -UNKNOWN.egg-info/SOURCES.txt -UNKNOWN.egg-info/dependency_links.txt -UNKNOWN.egg-info/top_level.txt \ No newline at end of file diff --git a/source/UNKNOWN.egg-info/top_level.txt b/source/UNKNOWN.egg-info/top_level.txt deleted file mode 100644 index 8b13789..0000000 --- a/source/UNKNOWN.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/source/database/collect_stats.py b/source/database/collect_stats.py deleted file mode 100644 index 40009e5..0000000 --- a/source/database/collect_stats.py +++ /dev/null @@ -1,108 +0,0 @@ -from datetime import datetime, timedelta -from pymodbus.client import ModbusSerialClient -from solaxx3.rs485 import SolaxX3 -import mysql.connector - -s = SolaxX3(port="/dev/ttyUSB0", baudrate=115200) - -database_ip = "172.17.7.77" - -if s.connect(): - s.read_all_registers() - - # read the stats from the inverter - battery_capacity = s.read("battery_capacity")[0] - feed_in_today = s.read("feed_in_energy_today")[0] - consumtion_today = s.read("consumption_energy_today")[0] - battery_charging = s.read("battery_power_charge1")[0] - grid_voltage_r = s.read("grid_voltage_r")[0] - grid_voltage_s = s.read("grid_voltage_s")[0] - grid_voltage_t = s.read("grid_voltage_t")[0] - run_mode = s.read("run_mode")[0] - time_count_down = s.read("time_count_down")[0] - inverter_ac_power = s.read("grid_power")[0] - etoday_togrid = s.read("energy_to_grid_today")[0] - solar_energy_today = s.read("solar_energy_today")[0] - echarge_today = s.read("echarge_today")[0] - energy_from_grid = s.read("energy_from_grid_meter")[0] - energy_to_grid = s.read("energy_to_grid_meter")[0] - power_to_ev = s.read("power_to_ev")[0] - feed_in_power = s.read("feed_in_power")[0] - output_energy_charge = s.read("output_energy_charge")[0] - output_energy_today = s.read("output_energy_charge_today")[0] - input_energy_today = s.read("input_energy_charge_today")[0] - power_dc1 = s.read("power_dc1")[0] - power_dc2 = s.read("power_dc2")[0] - total_power = power_dc1 + power_dc2 - uploadTime = s.read("rtc_datetime")[0] - uploadDate = uploadTime.date() - - timezone_difference_from_utc = 2 - uploadTime = uploadTime - timedelta(hours=timezone_difference_from_utc, minutes=0) - - # store the stats in the database - mydb = mysql.connector.connect( - host=database_ip, user="root", passwd="rootroot", database="solax" - ) - mycursor = mydb.cursor() - - try: - - # create the sql statement - sql = """REPLACE INTO solax_local ( - uploadTime, - inverter_status, - dc_solar_power, - grid_voltage_r, - grid_voltage_s, - grid_voltage_t, - battery_capacity, - battery_power, - feed_in_power, - time_count_down, - inverter_ac_power, - consumeenergy, - feedinenergy, - power_dc1, - power_dc2 - ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) - """ - - values = ( - uploadTime, - run_mode, - total_power, - grid_voltage_r, - grid_voltage_s, - grid_voltage_t, - battery_capacity, - battery_charging, - feed_in_power, - time_count_down, - inverter_ac_power, - energy_from_grid, - energy_to_grid, - power_dc1, - power_dc2, - ) - - mycursor.execute(sql, values) - mydb.commit() - - # update daily values - sql = """REPLACE INTO solax_daily ( - uploadDate, - feed_in, - total_yield - ) VALUES (%s, %s, %s) - """ - values = (uploadDate, feed_in_today, etoday_togrid) - mycursor.execute(sql, values) - mydb.commit() - - except mysql.connector.Error as error: - print("parameterized query failed {}".format(error)) - -else: - print("Cannot connect to the Modbus Server/Slave") - exit() diff --git a/source/database/data_source_db.py b/source/database/data_source_db.py new file mode 100644 index 0000000..5c30083 --- /dev/null +++ b/source/database/data_source_db.py @@ -0,0 +1,19 @@ +"""Module containing an abstract class.""" + +from abc import ABC, abstractmethod + + +class DataSourceDb(ABC): + """Abstract class representing a data source.""" + + @abstractmethod + def save_record(self): + """Transfer some data.""" + + raise NotImplementedError + + @abstractmethod + def bulk_save(self): + """Transfer multiple records of data.""" + + raise NotImplementedError diff --git a/source/database/mysql_data_source.py b/source/database/mysql_data_source.py new file mode 100644 index 0000000..60397f3 --- /dev/null +++ b/source/database/mysql_data_source.py @@ -0,0 +1,89 @@ +from typing import Any, Dict, List, Tuple + +import mysql.connector + +from .data_source_db import DataSourceDb + + +class MySQLDataSource(DataSourceDb): + def __init__(self, mysql_connection_info: Dict[str, str]) -> None: + self.user = mysql_connection_info["user"] + self.host = mysql_connection_info["host"] + self.password = mysql_connection_info["password"] + + def save_record( + self, + database: str, + tablename: str, + data: Dict[str, Any], + use_obj_connection: bool = False, + close_obj_connection: bool = True, + ): + query, values = self.create_query(tablename, data) + + if use_obj_connection: + try: + self.cursor.execute(query, values) + + if close_obj_connection: + self.db.commit() + self.db.close() + + except mysql.connector.Error: + self.db.commit() + self.db.close() + raise + + else: + db = mysql.connector.connect( + user=self.user, + host=self.host, + password=self.password, + database=database, + ) + try: + cursor = db.cursor() + cursor.execute(query, values) + db.commit() + db.close() + except mysql.connector.Error: + db.close() + raise + + def bulk_save(self, export_data: List[Dict[str, Any]]) -> None: + for index, unit in enumerate(export_data): + database, table_name, data = unit.values() + + if ( + not hasattr(self, "db") + or not hasattr(self, "cursor") + or not self.db.is_connected() + ): + self.db = mysql.connector.connect( + user=self.user, + host=self.host, + password=self.password, + database=database, + ) + try: + self.cursor = self.db.cursor() + except: + self.db.close() + raise + + self.save_record( + database, table_name, data, True, index + 1 == len(export_data) + ) + + def create_query(self, table_name: str, data: dict) -> Tuple[str, list]: + columns = list(data.keys()) + values = list(data.values()) + + query = ( + f"REPLACE INTO {table_name} (" + + ", ".join(columns) + + ") VALUES (" + + ", ".join(["%s"] * len(columns)) + + ")" + ) + return (query, values) diff --git a/source/database/read_and_save.py b/source/database/read_and_save.py new file mode 100644 index 0000000..76e19a0 --- /dev/null +++ b/source/database/read_and_save.py @@ -0,0 +1,93 @@ +"""Example of reading and saving some inverter registers.""" + +import sys +from datetime import datetime, timedelta +from os import environ + +from solaxx3.solaxx3 import SolaxX3 + +from .mysql_data_source import MySQLDataSource + + +def _get_datetime(inverter_time: datetime) -> datetime: + return inverter_time - timedelta(hours=TIMEZONE_OFFSET) + + +MYSQL_CONNECTION_INFO = { + "user": environ["MYSQL_DB_USERNAME"], + "host": environ["MYSQL_DB_HOST_IP"], + "password": environ["MYSQL_DB_PASSWORD"], +} +DATABASE = environ["MYSQL_DB_DATABASE"] +TIMEZONE_OFFSET = 2 + +s = SolaxX3(port="/dev/ttyUSB0", baudrate=115200) +if not s.connect(): + print("Could not connect to inverter") + sys.exit(1) + +s.read_all_registers() + +mysql_export_data = [ + { + "database": DATABASE, + "table": "solax_local", + "data": { + "uploadTime": _get_datetime(s.read("rtc_datetime")[0]), + "inverter_status": s.read("run_mode")[0], + "dc_solar_power": s.read("power_dc1")[0] + s.read("power_dc2")[0], + "grid_voltage_r": s.read("grid_voltage_r")[0], + "grid_voltage_s": s.read("grid_voltage_s")[0], + "grid_voltage_t": s.read("grid_voltage_t")[0], + "battery_capacity": s.read("battery_capacity")[0], + "battery_power": s.read("battery_power_charge1")[0], + "feed_in_power": s.read("feed_in_power")[0], + "time_count_down": s.read("time_count_down")[0], + "inverter_ac_power": s.read("grid_power")[0], + "consumeenergy": s.read("energy_from_grid_meter")[0], + "feedinenergy": s.read("energy_to_grid_meter")[0], + "power_dc1": s.read("power_dc1")[0], + "power_dc2": s.read("power_dc2")[0], + "inv_volt_r": s.read("inv_volt_r")[0], + "inv_volt_s": s.read("inv_volt_s")[0], + "inv_volt_t": s.read("inv_volt_t")[0], + "off_grid_power_active_r": s.read("off_grid_power_active_r")[0], + "off_grid_power_active_s": s.read("off_grid_power_active_s")[0], + "off_grid_power_active_t": s.read("off_grid_power_active_t")[0], + "grid_power_r": s.read("grid_power_r")[0], + "grid_power_s": s.read("grid_power_s")[0], + "grid_power_t": s.read("grid_power_t")[0], + }, + }, + { + "database": DATABASE, + "table": "solax_daily", + "data": { + "uploadDate": _get_datetime(s.read("rtc_datetime")[0]).date(), + "feed_in": s.read("feed_in_energy_today")[0], + "total_yield": s.read("energy_to_grid_today")[0], + }, + }, +] +mysql_data_source = MySQLDataSource(MYSQL_CONNECTION_INFO) + + +def bulk_save(): + """Save collected data.""" + + mysql_data_source.bulk_save(mysql_export_data) + + +def _transfer(index: int, **extras) -> None: + """Save a record of collected data.""" + + data_record = mysql_export_data[index] + database, tablename = data_record["database"], data_record["table"] + data = data_record["data"] + mysql_data_source.save_record(database, tablename, data, **extras) + + +save = bulk_save + +if __name__ == "__main__": + save() diff --git a/source/database/schema.sql b/source/database/schema.sql index 27ec77d..8c42b5f 100644 --- a/source/database/schema.sql +++ b/source/database/schema.sql @@ -1,25 +1,36 @@ -CREATE TABLE `solax_daily` ( - `uploadDate` date NOT NULL, - `feed_in` float(6,1) DEFAULT NULL, - `total_yield` float(6,1) DEFAULT NULL, - PRIMARY KEY (`uploadDate`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +CREATE TABLE solax_daily ( + uploadDate DATE NOT NULL, + feed_in FLOAT NULL, + total_yield FLOAT NULL +) +ENGINE=InnoDB +DEFAULT CHARSET=latin1; -CREATE TABLE `solax_local` ( - `uploadTime` datetime NOT NULL, - `inverter_status` tinyint(4) DEFAULT NULL, - `dc_solar_power` smallint(6) DEFAULT NULL, - `grid_voltage_r` smallint(6) DEFAULT NULL, - `grid_voltage_s` smallint(6) DEFAULT NULL, - `grid_voltage_t` smallint(6) DEFAULT NULL, - `battery_capacity` tinyint(4) DEFAULT NULL, - `battery_power` smallint(6) DEFAULT NULL, - `feed_in_power` smallint(6) DEFAULT NULL, - `time_count_down` smallint(6) DEFAULT NULL, - `inverter_ac_power` smallint(6) DEFAULT NULL, - `consumeenergy` float(7,1) DEFAULT NULL, - `feedinenergy` float(7,1) DEFAULT NULL, - `power_dc1` smallint(6) DEFAULT NULL, - `power_dc2` smallint(6) DEFAULT NULL, - PRIMARY KEY (`uploadTime`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +CREATE TABLE solax_local ( + uploadTime DATETIME NOT NULL, + inverter_status TINYINT NULL, + dc_solar_power SMALLINT NULL, + grid_voltage_r SMALLINT NULL, + grid_voltage_s SMALLINT NULL, + grid_voltage_t SMALLINT NULL, + battery_capacity TINYINT NULL, + battery_power SMALLINT NULL, + feed_in_power SMALLINT NULL, + time_count_down SMALLINT NULL, + inverter_ac_power SMALLINT NULL, + consumeenergy FLOAT NULL, + feedinenergy FLOAT NULL, + power_dc1 SMALLINT NULL, + power_dc2 SMALLINT NULL, + inv_volt_r SMALLINT NULL, + inv_volt_s SMALLINT NULL, + inv_volt_t SMALLINT NULL, + off_grid_power_active_r INTEGER NULL, + off_grid_power_active_s INTEGER NULL, + off_grid_power_active_t INTEGER NULL, + grid_power_r INTEGER NULL, + grid_power_s INTEGER NULL, + grid_power_t INTEGER NULL +) +ENGINE=InnoDB +DEFAULT CHARSET=latin1; \ No newline at end of file diff --git a/source/dist/solaxx3-1.0.2-py3-none-any.whl b/source/dist/solaxx3-1.0.2-py3-none-any.whl new file mode 100644 index 0000000..f70402e Binary files /dev/null and b/source/dist/solaxx3-1.0.2-py3-none-any.whl differ diff --git a/source/dist/solaxx3-1.0.2.tar.gz b/source/dist/solaxx3-1.0.2.tar.gz new file mode 100644 index 0000000..57e26cd Binary files /dev/null and b/source/dist/solaxx3-1.0.2.tar.gz differ diff --git a/source/example.py b/source/example.py index 66ef8d3..51d6506 100644 --- a/source/example.py +++ b/source/example.py @@ -1,22 +1,21 @@ +"""Sample program for reading and saving some inverter register values.""" + +#from solaxx3.solaxx3 import SolaxX3 from solaxx3.rs485 import SolaxX3 # adjust the serial port and baud rate as necessary -s = SolaxX3(port="/dev/ttyUSB0", baudrate=115200) +s = SolaxX3(port="/dev/ttyUSB0", baudrate=19200) if s.connect(): s.read_all_registers() - print(s._input_registers_values_list) - exit() - available_stats = s.list_register_names() for stat in available_stats: print(stat, f" {s.read(stat)}") battery_temperature = s.read("temperature_battery") print(f"\n\nBattery temperature: {s.read('temperature_battery')}") - - + work_mode = s.read("work_mode") + print(work_mode) else: print("Cannot connect to the Modbus Server/Slave") - exit() \ No newline at end of file diff --git a/source/pyproject.toml b/source/pyproject.toml index 7513639..9fed4bc 100644 --- a/source/pyproject.toml +++ b/source/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "solaxx3" -version = "0.1.0" +version = "1.0.2" description = "Read Solax X3 inverter registers via modbus interface (RS-485)" readme = "README.md" authors = [{ name = "Flavius Moldovan", email = "mkfam@protonmail.com" }] @@ -15,6 +15,11 @@ classifiers = [ "License :: OSI Approved :: MIT License", "Programming Language :: Python", "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", ] keywords = ["Solax", "solax-x3", "solaxx3", "solar inverter", "RTU", "MODBUS"] dependencies = [ diff --git a/source/src/.DS_Store b/source/src/.DS_Store index f422855..8b49799 100644 Binary files a/source/src/.DS_Store and b/source/src/.DS_Store differ diff --git a/source/src/solaxx3.egg-info/PKG-INFO b/source/src/solaxx3.egg-info/PKG-INFO new file mode 100644 index 0000000..e21fe61 --- /dev/null +++ b/source/src/solaxx3.egg-info/PKG-INFO @@ -0,0 +1,82 @@ +Metadata-Version: 2.1 +Name: solaxx3 +Version: 1.0.2 +Summary: Read Solax X3 inverter registers via modbus interface (RS-485) +Author-email: Flavius Moldovan +License: The MIT License (MIT) + Copyright © 2022 + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +Project-URL: Homepage, https://github.com/mkfam7/solaxx3 +Keywords: Solax,solax-x3,solaxx3,solar inverter,RTU,MODBUS +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Requires-Python: >=3.8 +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: pymodbus[serial]>=3.0.0 + +![Build badge](https://github.com/mkfam7/solaxx3/actions/workflows/python-package.yml/badge.svg) + + + +# solax-x3 +#### Read in real-time all parameters provided by Solax X3 solar inverter via its Modbus S-485 serial interface. + +
+ +## Prerequisites + +* Solax X3 inverter +* Modbus RS-485 serial adapter/interface +* [Modbus cable](https://github.com/mkfam7/solaxx3/blob/main/diagrams/rs485_cable.png) +* python version >= 3.8 +* This python module + +## Installation + +``` +pip install solaxx3 +``` + +## Usage + + +``` +from solaxx3.solaxx3 import SolaxX3 + +# adjust the serial port and baud rate as necessary +s = SolaxX3(port="/dev/ttyUSB0", baudrate=115200) + +if s.connect(): + s.read_all_registers() + + available_stats = s.list_register_names() + for stat in available_stats: + print(stat) + + battery_temperature = s.read("temperature_battery") + print(f"\n\nBattery temperature: {s.read('temperature_battery')}") + + +else: + print("Cannot connect to the Modbus Server/Slave") + exit() + + +``` + +Project Link: [https://github.com/mkfam7/solaxx3](https://github.com/mkfam7/solaxx3) + + + diff --git a/source/src/solaxx3.egg-info/SOURCES.txt b/source/src/solaxx3.egg-info/SOURCES.txt new file mode 100644 index 0000000..73387c6 --- /dev/null +++ b/source/src/solaxx3.egg-info/SOURCES.txt @@ -0,0 +1,16 @@ +LICENSE +README.md +pyproject.toml +setup.py +src/solaxx3/__init__.py +src/solaxx3/solax_registers_info.py +src/solaxx3/solaxx3.py +src/solaxx3/utils.py +src/solaxx3.egg-info/PKG-INFO +src/solaxx3.egg-info/SOURCES.txt +src/solaxx3.egg-info/dependency_links.txt +src/solaxx3.egg-info/requires.txt +src/solaxx3.egg-info/top_level.txt +tests/test_mysql_connector.py +tests/test_register_data.py +tests/test_solaxx3.py \ No newline at end of file diff --git a/source/UNKNOWN.egg-info/dependency_links.txt b/source/src/solaxx3.egg-info/dependency_links.txt similarity index 100% rename from source/UNKNOWN.egg-info/dependency_links.txt rename to source/src/solaxx3.egg-info/dependency_links.txt diff --git a/source/src/solaxx3.egg-info/requires.txt b/source/src/solaxx3.egg-info/requires.txt new file mode 100644 index 0000000..6fb8063 --- /dev/null +++ b/source/src/solaxx3.egg-info/requires.txt @@ -0,0 +1 @@ +pymodbus[serial]>=3.0.0 diff --git a/source/src/solaxx3.egg-info/top_level.txt b/source/src/solaxx3.egg-info/top_level.txt new file mode 100644 index 0000000..8de2371 --- /dev/null +++ b/source/src/solaxx3.egg-info/top_level.txt @@ -0,0 +1 @@ +solaxx3 diff --git a/source/src/solaxx3/__init__.py b/source/src/solaxx3/__init__.py index 5935f36..49c9fdb 100644 --- a/source/src/solaxx3/__init__.py +++ b/source/src/solaxx3/__init__.py @@ -1,2 +1,3 @@ -# Version of the Solax RTU package -__version__ = "0.0.6" +"""Version of the Solax RTU package""" + +__version__ = "1.0.2" diff --git a/source/src/solaxx3/registers.py b/source/src/solaxx3/registers.py deleted file mode 100644 index 98c27fe..0000000 --- a/source/src/solaxx3/registers.py +++ /dev/null @@ -1,1619 +0,0 @@ -class SolaxRegistersInfo: - __registers = { - # input registers - "grid_voltage": { - "address": 0x0000, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "V", - "data_length": 1, - "description": "Grid voltage", - }, - "grid_current": { - "address": 0x0001, - "register_type": "input", - "data_format": "int16", - "si_adj": 10, - "signed": True, - "data_unit": "A", - "data_length": 1, - "description": "Grid current", - }, - "grid_power": { - "address": 0x0002, - "register_type": "input", - "data_format": "int16", - "si_adj": 1, - "signed": True, - "data_unit": "W", - "data_length": 1, - "description": "Grid power", - }, - "pv_voltage_1": { - "address": 0x0003, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "V", - "data_length": 1, - "description": "PV Voltage 1", - }, - "pv_voltage_2": { - "address": 0x0004, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "V", - "data_length": 1, - "description": "PV Voltage 2", - }, - "pv_current_1": { - "address": 0x0005, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "A", - "data_length": 1, - "description": "PV Current 1", - }, - "pv_current_2": { - "address": 0x0006, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "A", - "data_length": 1, - "description": "PV Current 2", - }, - "grid_frequency": { - "address": 0x0007, - "register_type": "input", - "data_format": "uint16", - "si_adj": 100, - "signed": False, - "data_unit": "Hz", - "data_length": 1, - "description": "Grid frequency", - }, - "radiator_temperature": { - "address": 0x0008, - "register_type": "input", - "data_format": "int16", - "si_adj": 1, - "signed": True, - "data_unit": "C", - "data_length": 1, - "description": "Radiator temperature", - }, - "run_mode": { - "address": 0x0009, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "", - "data_length": 1, - "description": "Inverter status", - }, - "power_dc1": { - "address": 0x000A, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "W", - "data_length": 1, - "description": "DC Power String 1", - }, - "power_dc2": { - "address": 0x000B, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "W", - "data_length": 1, - "description": "DC Power String 2", - }, - "temperature_fault_value": { - "address": 0x000C, - "register_type": "input", - "data_format": "int16", - "si_adj": 1, - "signed": True, - "data_unit": "C", - "data_length": 1, - "description": "Temperature fault value", - }, - "pv1_volt_fault_value": { - "address": 0x000D, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "V", - "data_length": 1, - "description": "PV1 volt fault value", - }, - "pv2_volt_fault_value": { - "address": 0x000E, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "V", - "data_length": 1, - "description": "PV2 volt fault value", - }, - "gfci_fault_value": { - "address": 0x000F, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1000, - "signed": False, - "data_unit": "A", - "data_length": 1, - "description": "GFCI fault value", - }, - "grid_fault_value": { - "address": 0x0010, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "V", - "data_length": 1, - "description": "Grid fault value", - }, - "grid_freq__fault__value_t": { - "address": 0x0011, - "register_type": "input", - "data_format": "uint16", - "si_adj": 100, - "signed": False, - "data_unit": "Hz", - "data_length": 1, - "description": "Grid frequency fault value t", - }, - "dci_fault_value": { - "address": 0x0012, - "register_type": "input", - "data_format": "uint16", - "si_adj": 100, - "signed": False, - "data_unit": "A", - "data_length": 1, - "description": "DCI fault value", - }, - "time_count_down": { - "address": 0x0013, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1000, - "signed": False, - "data_unit": "seconds", - "data_length": 1, - "description": "Time remaining until the current operation finishes (e.g. checking)", - }, - "battery_voltage_charge_1": { - "address": 0x0014, - "register_type": "input", - "data_format": "int16", - "si_adj": 10, - "signed": True, - "data_unit": "V", - "data_length": 1, - "description": "Battery voltage charge 1", - }, - "battery_current_charge_1": { - "address": 0x0015, - "register_type": "input", - "data_format": "int16", - "si_adj": 10, - "signed": True, - "data_unit": "V", - "data_length": 1, - "description": "Battery current charge 1", - }, - "battery_power_charge1": { - "address": 0x0016, - "register_type": "input", - "data_format": "int16", - "si_adj": 1, - "signed": True, - "data_unit": "W", - "data_length": 1, - "description": "Battery power charge 1", - }, - "bms_connect_state": { - "address": 0x0017, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "", - "data_length": 1, - "description": "BMS connect state", - }, - "temperature_battery": { - "address": 0x0018, - "register_type": "input", - "data_format": "int16", - "si_adj": 1, - "signed": True, - "data_unit": "C", - "data_length": 1, - "description": "Temperature of the battery", - }, - "bdc_status": { - "address": 0x0019, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "N/A", - "data_length": 1, - "description": "BDC Status", - }, - "grid_status": { - "address": 0x001A, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "N/A", - "data_length": 1, - "description": "Grid Status: 0-OnGrid, 1-OffGrid", - }, - "mppt_count": { - "address": 0x001B, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "N/A", - "data_length": 1, - "description": "MPPT Count", - }, - "battery_capacity": { - "address": 0x001C, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "%", - "data_length": 1, - "description": "Battery charge percentage", - }, - "output_energy_charge": { - "address": 0x001D, - "register_type": "input", - "data_format": "uint32", - "si_adj": 10, - "signed": False, - "data_unit": "KWh", - "data_length": 2, - "description": "Output energy charge", - }, - "output_energy_charge_today": { - "address": 0x0020, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "KWh", - "data_length": 1, - "description": "Output energy charge today", - }, - "input_energy_charge": { - "address": 0x0021, - "register_type": "input", - "data_format": "uint32", - "si_adj": 10, - "signed": False, - "data_unit": "KWh", - "data_length": 2, - "description": "Input energy charge", - }, - "input_energy_charge_today": { - "address": 0x0023, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "KWh", - "data_length": 1, - "description": "Input energy charge today", - }, - "bms_charge_max_current": { - "address": 0x0024, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "A", - "data_length": 1, - "description": "BMS charge max current", - }, - "bms_discharge_max_current": { - "address": 0x0025, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "A", - "data_length": 1, - "description": "BMS discharge max current", - }, - "power_to_ev": { - "address": 0x0026, - "register_type": "input", - "data_format": "int32", - "si_adj": 1, - "signed": True, - "data_unit": "W", - "data_length": 2, - "description": "Power to EV", - }, - "ref_power_to_ev": { - "address": 0x0028, - "register_type": "input", - "data_format": "int16", - "si_adj": 1, - "signed": False, - "data_unit": "W", - "data_length": 1, - "description": "ref_power_to_ev", - }, - "pcs_major_fault": { - "address": 0x003E, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "N/A", - "data_length": 1, - "description": "PCS Major Fault", - }, - "battery_major_fault": { - "address": 0x003F, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "N/A", - "data_length": 1, - "description": "Battery Major Fault", - }, - "inv_fault_message": { - "address": 0x0040, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "N/A", - "data_length": 2, - "description": "Inverter Fault Message: X1:Table 2-4, X2:Table 2-3", - }, - "mgr_fault_message": { - "address": 0x0043, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "N/A", - "data_length": 1, - "description": "Manager Fault Message: Table 2-5", - }, - "bat_bms_fault_message": { - "address": 0x0044, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "N/A", - "data_length": 2, - "description": "Battery BMS Fault Message - Table 2-6", - }, - "feed_in_power": { - "address": 0x0046, - "register_type": "input", - "data_format": "int32", - "si_adj": 1, - "signed": True, - "data_unit": "W ", - "data_length": 2, - "description": "Feed-in power obtained from Meter or CT", - }, - "energy_to_grid_meter": { - "address": 0x0048, - "register_type": "input", - "data_format": "uint32", - "si_adj": 1, - "signed": False, - "data_unit": "KWh", - "si_adj": 100, - "data_length": 2, - "description": "Energy to grid meter value", - }, - "energy_from_grid_meter": { - "address": 0x004A, - "register_type": "input", - "data_format": "uint32", - "si_adj": 100, - "signed": False, - "data_unit": "KWh", - "data_length": 2, - "description": "Energy from grid meter value", - }, - "off_grid_voltage": { - "address": 0x004C, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "V", - "data_length": 1, - "description": "Off-Grid Voltage X1", - }, - "off_grid_current": { - "address": 0x004D, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "A", - "data_length": 1, - "description": "Off-Grid Current X1", - }, - "off_grid_power": { - "address": 0x004E, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "VA", - "data_length": 1, - "description": "Off Grid Power X1", - }, - "off_grid_frequency": { - "address": 0x004F, - "register_type": "input", - "data_format": "uint16", - "si_adj": 100, - "signed": False, - "data_unit": "Hz", - "data_length": 1, - "description": "Off-Grid Frequency", - }, - "energy_to_grid_today": { - "address": 0x0050, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "KWh", - "data_length": 1, - "description": "The quantity of energy sent to grid today", - }, - "energy_to_grid_total": { - "address": 0x0052, - "register_type": "input", - "data_format": "uint32", - "si_adj": 10, - "signed": False, - "data_unit": "KWh", - "data_length": 2, - "description": "Total energy sent to grid", - }, - "lock_state": { - "address": 0x0054, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "N/A", - "data_length": 1, - "description": "Lock State", - }, - "bus_volt": { - "address": 0x0066, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "V", - "data_length": 1, - "description": "Bus Volt", - }, - "w_dcv_fault_val": { - "address": 0x0067, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "V", - "data_length": 1, - "description": "W DCV Fault Value", - }, - "w_overload_fault_val": { - "address": 0x0068, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "W", - "data_length": 1, - "description": "W Overload Fault Value", - }, - "w_battery_volt_fault_val": { - "address": 0x0069, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "V", - "data_length": 1, - "description": "W Battery Volt Fault Value", - }, - "grid_voltage_r": { - "address": 0x006A, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "V", - "data_length": 1, - "description": "Grid voltage for R phase", - }, - "grid_current_r": { - "address": 0x006B, - "register_type": "input", - "data_format": "int16", - "si_adj": 10, - "signed": True, - "data_unit": "A", - "data_length": 1, - "description": "Grid Current R Phase", - }, - "grid_power_r": { - "address": 0x006C, - "register_type": "input", - "data_format": "int16", - "si_adj": 1, - "signed": True, - "data_unit": "W", - "data_length": 1, - "description": "Grid Power R Phase", - }, - "grid_frequency_r": { - "address": 0x006D, - "register_type": "input", - "data_format": "uint16", - "si_adj": 100, - "signed": False, - "data_unit": "Hz", - "data_length": 1, - "description": "Grid Frequency R Phase", - }, - "grid_voltage_s": { - "address": 0x006E, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "V", - "data_length": 1, - "description": "Grid voltage for S phase", - }, - "grid_current_s": { - "address": 0x006F, - "register_type": "input", - "data_format": "int16", - "si_adj": 10, - "signed": True, - "data_unit": "A", - "data_length": 1, - "description": "Grid Current S Phase", - }, - "grid_power_s": { - "address": 0x0070, - "register_type": "input", - "data_format": "int16", - "si_adj": 1, - "signed": True, - "data_unit": "W", - "data_length": 1, - "description": "Grid Power S Phase", - }, - "grid_frequency_s": { - "address": 0x0071, - "register_type": "input", - "data_format": "uint16", - "si_adj": 100, - "signed": False, - "data_unit": "Hz", - "data_length": 1, - "description": "Grid Frequency S", - }, - "grid_voltage_t": { - "address": 0x0072, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "V", - "data_length": 1, - "description": "Grid voltage for T phase", - }, - "grid_current_t": { - "address": 0x0073, - "register_type": "input", - "data_format": "int16", - "si_adj": 10, - "signed": True, - "data_unit": "A", - "data_length": 1, - "description": "Grid Current T Phase", - }, - "grid_power_t": { - "address": 0x0074, - "register_type": "input", - "data_format": "int16", - "si_adj": 1, - "signed": True, - "data_unit": "W", - "data_length": 1, - "description": "Grid Power T Phase", - }, - "grid_frequency_t": { - "address": 0x0075, - "register_type": "input", - "data_format": "uint16", - "si_adj": 100, - "signed": False, - "data_unit": "Hz", - "data_length": 1, - "description": "Grid Frequency T Phase", - }, - "off_grid_volt_r": { - "address": 0x0076, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "V", - "data_length": 1, - "description": "Off-Grid Volt R Phase", - }, - "off_grid_current_r": { - "address": 0x0077, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "A", - "data_length": 1, - "description": "Off-Grid Current R Phase", - }, - "off_grid_power_active_r": { - "address": 0x0078, - "register_type": "input", - "data_format": "int16", - "si_adj": 1, - "signed": True, - "data_unit": "W", - "data_length": 1, - "description": "Off-Grid Active Power R Phase", - }, - "off_grid_power_s_r": { - "address": 0x0079, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "VA", - "data_length": 1, - "description": "Off-Grid S Power R Phase", - }, - "off-grid-volt-s": { - "address": 0x007A, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "V", - "data_length": 1, - "description": "Off-Grid Volt S Phase", - }, - "off_grid_current_s": { - "address": 0x007B, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "A", - "data_length": 1, - "description": "Off-Grid Current S Phase", - }, - "off_grid_power_active_s": { - "address": 0x007C, - "register_type": "input", - "data_format": "int16", - "si_adj": 1, - "signed": True, - "data_unit": "W", - "data_length": 1, - "description": "Off-Grid Active Power S Phase", - }, - "off_grid_power_s_s": { - "address": 0x007D, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "VA", - "data_length": 1, - "description": "Off-Grid S Power S Phase", - }, - "off_grid_volt_t": { - "address": 0x007E, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "V", - "data_length": 1, - "description": "Off-Grid Volt T Phase", - }, - "off_grid_current_t": { - "address": 0x007F, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": True, - "data_unit": "A", - "data_length": 1, - "description": "Off-Grid Current T Phase", - }, - "off_grid_power_active_t": { - "address": 0x0080, - "register_type": "input", - "data_format": "int16", - "si_adj": 1, - "signed": True, - "data_unit": "W", - "data_length": 1, - "description": "Off-Grid Active Power T Phase", - }, - "off_grid_power_s_t": { - "address": 0x0081, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "VA", - "data_length": 1, - "description": "Off-Grid S Power T Phase", - }, - "feedin_power_r_phase": { - "address": 0x0082, - "register_type": "input", - "data_format": "int32", - "si_adj": 1, - "signed": True, - "data_unit": "W", - "data_length": 2, - "description": "Feedin Power R Phase", - }, - "feedin_power_s_phase": { - "address": 0x0084, - "register_type": "input", - "data_format": "int32", - "si_adj": 1, - "signed": True, - "data_unit": "W", - "data_length": 2, - "description": "Feedin Power S Phase", - }, - "feedin_power_t_phase": { - "address": 0x0086, - "register_type": "input", - "data_format": "int32", - "si_adj": 1, - "signed": True, - "data_unit": "W", - "data_length": 2, - "description": "Feedin Power T Phase", - }, - "ongrid_run_time": { - "address": 0x0088, - "register_type": "input", - "data_format": "int32", - "si_adj": 10, - "signed": True, - "data_unit": "hour", - "data_length": 2, - "description": "On-Grid Run Time", - }, - "offgrid_run_time": { - "address": 0x008A, - "register_type": "input", - "data_format": "int32", - "si_adj": 10, - "signed": True, - "data_unit": "hour", - "data_length": 2, - "description": "Off-Grid Run Time", - }, - "offgrid_yield_total": { - "address": 0x008E, - "register_type": "input", - "data_format": "uint32", - "si_adj": 10, - "signed": False, - "data_unit": "KWh", - "data_length": 2, - "description": "Off-Grid Yield Total", - }, - "offgrid_yield_today": { - "address": 0x0090, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "KWh", - "data_length": 1, - "description": "Off-Grid Yield Today", - }, - "echarge_today": { - "address": 0x0091, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "KWh", - "data_length": 1, - "description": "Echarge energy today", - }, - "echarge_total": { - "address": 0x0092, - "register_type": "input", - "data_format": "uint32", - "si_adj": 10, - "signed": False, - "data_unit": "KWh", - "data_length": 2, - "description": "Echarge Total", - }, - "solar_energy_total": { - "address": 0x0094, - "register_type": "input", - "data_format": "uint32", - "si_adj": 10, - "signed": False, - "data_unit": "KWh", - "data_length": 2, - "description": "Total solar energy", - }, - "solar_energy_today": { - "address": 0x0096, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "KWh", - "data_length": 1, - "description": "Solar energy today", - }, - "feed_in_energy_today": { - "address": 0x0098, - "register_type": "input", - "data_format": "uint32", - "si_adj": 100, - "signed": False, - "data_unit": "KWh", - "data_length": 2, - "description": "Energy to grid meter for today", - }, - "consumption_energy_today": { - "address": 0x009A, - "register_type": "input", - "data_format": "uint32", - "si_adj": 100, - "signed": False, - "data_unit": "KWh", - "data_length": 2, - "description": "Energy from grid meter for today", - }, - "inv_volt_r": { - "address": 0x009C, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "V", - "data_length": 1, - "description": "Inverter Volt R Phase", - }, - "inv_volt_s": { - "address": 0x009D, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "V", - "data_length": 1, - "description": "Inverter Volt S Phase", - }, - "inv_volt_t": { - "address": 0x009E, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "V", - "data_length": 1, - "description": "Inverter Volt T Phase", - }, - "feedin_power_meter_2": { - "address": 0x00A8, - "register_type": "input", - "data_format": "int32", - "si_adj": 1, - "signed": True, - "data_unit": "W", - "data_length": 2, - "description": "Power to Grid Meter 2", - }, - "feedin_energy_total_meter_2": { - "address": 0x00AA, - "register_type": "input", - "data_format": "uint32", - "si_adj": 100, - "signed": False, - "data_unit": "KWh", - "data_length": 2, - "description": "Energy to the grid", - }, - "consum_energy_total_meter_2": { - "address": 0x00AC, - "register_type": "input", - "data_format": "uint32", - "si_adj": 100, - "signed": False, - "data_unit": "KWh", - "data_length": 2, - "description": "Energy from grid", - }, - "feedin_energy_today_meter_2": { - "address": 0x00AE, - "register_type": "input", - "data_format": "uint32", - "si_adj": 100, - "signed": False, - "data_unit": "KWh", - "data_length": 2, - "description": "Energy to grid", - }, - "consum_energy_today_meter_2": { - "address": 0x00B0, - "register_type": "input", - "data_format": "uint32", - "si_adj": 100, - "signed": False, - "data_unit": "KWh", - "data_length": 2, - "description": "Energy from grid", - }, - "feedin_power_r_phase_meter_2": { - "address": 0x00B2, - "register_type": "input", - "data_format": "int32", - "si_adj": 1, - "signed": True, - "data_unit": "W", - "data_length": 2, - "description": "Feedin Power R Phase Meter 2", - }, - "feedin_power_s_phase_meter_2": { - "address": 0x00B4, - "register_type": "input", - "data_format": "int32", - "si_adj": 1, - "signed": True, - "data_unit": "W", - "data_length": 2, - "description": "Feedin Power S Phase Meter 2", - }, - "feedin_power_t_phase_meter_2": { - "address": 0x00B6, - "register_type": "input", - "data_format": "int32", - "si_adj": 1, - "signed": True, - "data_unit": "W", - "data_length": 2, - "description": "Feedin Power T Phase Meter 2", - }, - "meter_1_communication_state": { - "address": 0x00B8, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "N/A", - "data_length": 1, - "description": "Meter 1 Communication State: 0:Com Error, 1:Normal", - }, - "meter_2_communication_state": { - "address": 0x00B9, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "N/A", - "data_length": 1, - "description": "Meter 2 Communication State: 0:Com Error, 1:Normal", - }, - "battery_tem_high": { - "address": 0x00BA, - "register_type": "input", - "data_format": "int16", - "si_adj": 10, - "signed": True, - "data_unit": "C", - "data_length": 1, - "description": "Battery Temperature High", - }, - "battery_tem_low": { - "address": 0x00BB, - "register_type": "input", - "data_format": "int16", - "si_adj": 10, - "signed": True, - "data_unit": "C", - "data_length": 1, - "description": "Battery Temperature Low", - }, - "cell_voltage_high": { - "address": 0x00BC, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1000, - "signed": False, - "data_unit": "V", - "data_length": 1, - "description": "Cell Voltage High", - }, - "cell_voltage_low": { - "address": 0x00BD, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1000, - "signed": True, - "data_unit": "V", - "data_length": 1, - "description": "Cell Voltage Low", - }, - "bms_user_soc": { - "address": 0x00BE, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "%", - "data_length": 1, - "description": "BMS User SOC", - }, - "bms_user_soh": { - "address": 0x00BF, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "%", - "data_length": 1, - "description": "BMS User SOH", - }, - "grid_reactive_power_total_meter": { - "address": 0x00C0, - "register_type": "input", - "data_format": "int16", - "si_adj": 1, - "signed": True, - "data_unit": "Var", - "data_length": 1, - "description": "Grid Reactive Power Total Meter", - }, - "grid_reactive_power_r_meter": { - "address": 0x00C1, - "register_type": "input", - "data_format": "int16", - "si_adj": 1, - "signed": True, - "data_unit": "Var", - "data_length": 1, - "description": "Grid Reactive Power Phase R Meter", - }, - "grid_reactive_power_s_meter": { - "address": 0x00C2, - "register_type": "input", - "data_format": "int16", - "si_adj": 1, - "signed": True, - "data_unit": "Var", - "data_length": 1, - "description": "Grid Reactive Power Phase S Meter", - }, - "grid_reactive_power_t_meter": { - "address": 0x00C3, - "register_type": "input", - "data_format": "int16", - "si_adj": 1, - "signed": True, - "data_unit": "Var", - "data_length": 1, - "description": "Grid Reactive Power Phase T Meter", - }, - "grid_power_factor_total_meter": { - "address": 0x00C4, - "register_type": "input", - "data_format": "int16", - "si_adj": 100, - "signed": True, - "data_unit": "N/A", - "data_length": 1, - "description": "Grid Power Factor Total Meter", - }, - "grid_power_factor_r_meter": { - "address": 0x00C5, - "register_type": "input", - "data_format": "int16", - "si_adj": 100, - "signed": True, - "data_unit": "N/A", - "data_length": 1, - "description": "Grid Power Factor Phase R Meter", - }, - "grid_power_factor_s_meter": { - "address": 0x00C6, - "register_type": "input", - "data_format": "int16", - "si_adj": 100, - "signed": True, - "data_unit": "N/A", - "data_length": 1, - "description": "Grid Power Factor Phase S Meter", - }, - "grid_power_factor_t_meter": { - "address": 0x00C7, - "register_type": "input", - "data_format": "int16", - "si_adj": 100, - "signed": True, - "data_unit": "N/A", - "data_length": 1, - "description": "Grid Power Factor Phase T Meter", - }, - "grid_frequency_meter": { - "address": 0x00C8, - "register_type": "input", - "data_format": "uint16", - "si_adj": 100, - "signed": False, - "data_unit": "Hz", - "data_length": 1, - "description": "Grid Frequency Meter", - }, - "grid_voltage_total_meter": { - "address": 0x00C9, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "V", - "data_length": 1, - "description": "Grid Voltage Total Meter", - }, - "grid_voltage_r_meter": { - "address": 0x00CA, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "V", - "data_length": 1, - "description": "Grid Voltage R Meter", - }, - "grid_voltage_s_meter": { - "address": 0x00CB, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "V", - "data_length": 1, - "description": "Grid Voltage S Meter", - }, - "grid_voltage_t_meter": { - "address": 0x00CC, - "register_type": "input", - "data_format": "uint16", - "si_adj": 10, - "signed": False, - "data_unit": "V", - "data_length": 1, - "description": "Grid Voltage T Meter", - }, - "grid_current_total_meter": { - "address": 0x00CD, - "register_type": "input", - "data_format": "int16", - "si_adj": 10, - "signed": True, - "data_unit": "A", - "data_length": 1, - "description": "Grid Current Total Meter", - }, - "grid_current_r_meter": { - "address": 0x00CE, - "register_type": "input", - "data_format": "int16", - "si_adj": 10, - "signed": True, - "data_unit": "A", - "data_length": 1, - "description": "Grid Current R Meter", - }, - "grid_current_s_meter": { - "address": 0x00CF, - "register_type": "input", - "data_format": "int16", - "si_adj": 10, - "signed": True, - "data_unit": "A", - "data_length": 1, - "description": "Grid Current S Meter", - }, - "grid_current_t_meter": { - "address": 0x00D0, - "register_type": "input", - "data_format": "int16", - "si_adj": 10, - "signed": True, - "data_unit": "A", - "data_length": 1, - "description": "Grid Current T Meter", - }, - "modbus_power_control": { - "address": 0x0100, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "N/A", - "data_length": 1, - "description": "Modbus Power Control: 0:disable remote contro, 1:enable power control, 2:enable electric quality control, 3:enable SOC target control", - }, - "target_finish_tag": { - "address": 0x0101, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "N/A", - "data_length": 1, - "description": "Target Finish Tag: 0:unfinished, 1:finished", - }, - "active_power_target": { - "address": 0x0102, - "register_type": "input", - "data_format": "int32", - "si_adj": 1, - "signed": True, - "data_unit": "W", - "data_length": 2, - "description": "Active Power Target", - }, - "w_reactive_power_target": { - "address": 0x0104, - "register_type": "input", - "data_format": "int32", - "si_adj": 1, - "signed": True, - "data_unit": "Var", - "data_length": 2, - "description": "W Reactive Power Target", - }, - "w_active_power_real": { - "address": 0x0106, - "register_type": "input", - "data_format": "int32", - "si_adj": 1, - "signed": True, - "data_unit": "W", - "data_length": 2, - "description": "W Active Power Real", - }, - "w_reactive_power_real": { - "address": 0x0108, - "register_type": "input", - "data_format": "int32", - "si_adj": 1, - "signed": True, - "data_unit": "Var", - "data_length": 2, - "description": "W Reactive Power Real", - }, - "w_active_power_upper": { - "address": 0x010A, - "register_type": "input", - "data_format": "int32", - "si_adj": 1, - "signed": True, - "data_unit": "W", - "data_length": 2, - "description": "W Active Power Upper", - }, - "w_active_power_lower": { - "address": 0x010C, - "register_type": "input", - "data_format": "int32", - "si_adj": 1, - "signed": True, - "data_unit": "W", - "data_length": 2, - "description": "W Active Power Lower", - }, - "w_reactive_power_upper": { - "address": 0x010E, - "register_type": "input", - "data_format": "int32", - "si_adj": 1, - "signed": True, - "data_unit": "Var", - "data_length": 2, - "description": "W Reactive Power Upper", - }, - "w_reactive_power_lower": { - "address": 0x0110, - "register_type": "input", - "data_format": "int32", - "si_adj": 1, - "signed": True, - "data_unit": "Var", - "data_length": 2, - "description": "W Reactive Power Lower", - }, - "target_energy": { - "address": 0x0112, - "register_type": "input", - "data_format": "int32", - "si_adj": 1, - "signed": True, - "data_unit": "Wh", - "data_length": 2, - "description": "Target Energy", - }, - "charge_discharge_power": { - "address": 0x0114, - "register_type": "input", - "data_format": "int32", - "si_adj": 1, - "signed": True, - "data_unit": "W", - "data_length": 2, - "description": "Charge Discharge Power", - }, - "chargeable_electric_capacity": { - "address": 0x0116, - "register_type": "input", - "data_format": "uint32", - "si_adj": 1, - "signed": False, - "data_unit": "Wh", - "data_length": 2, - "description": "Chargeable Electric Capacity", - }, - "dischargeable_electric_capacity": { - "address": 0x0118, - "register_type": "input", - "data_format": "uint32", - "si_adj": 1, - "signed": False, - "data_unit": "Wh", - "data_length": 2, - "description": "Dischargeable Electric Capacity", - }, - "time_of_duration": { - "address": 0x011A, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "second", - "data_length": 1, - "description": "time_of_duration", - }, - "target_soc": { - "address": 0x011B, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "%", - "data_length": 1, - "description": "Target SOC", - }, - "soc_upper": { - "address": 0x011C, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "%", - "data_length": 1, - "description": "SOC Upper", - }, - "soc_lower": { - "address": 0x011D, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "%", - "data_length": 1, - "description": "SOC Lower", - }, - "remote_control_timeout": { - "address": 0x011E, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "second", - "data_length": 1, - "description": "Remote Control Timeout: 4-65535", - }, - "w_battery_force_charge_flag": { - "address": 0x011F, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "N/A", - "data_length": 1, - "description": "W Battery Force Charge Flag: 0:No Action, 1:Force Charge", - }, - "w_bms_relay_state": { - "address": 0x0120, - "register_type": "input", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_unit": "N/A", - "data_length": 1, - "description": "W BMS Relay State: 0:OFF, 1:ON", - }, - # holding registers - "serial_number": { - "address": 0x0000, - "register_type": "holding", - "data_format": "varchar", - "signed": False, - "data_length": 7, - "description": "Inverter' serial number", - }, - "firmware_version_dsp": { - "address": 0x007D, - "register_type": "holding", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_length": 1, - "description": "Firmware version DSP", - }, - "hardware_version_dsp": { - "address": 0x007E, - "register_type": "holding", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_length": 1, - "description": "Hardware version DSP", - }, - "firmware_version_modbus_rtu": { - "address": 0x0082, - "register_type": "holding", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_length": 1, - "description": "Firmware version MODBUS RTU", - }, - "firmware_version_arm": { - "address": 0x0083, - "register_type": "holding", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_length": 1, - "description": "Firmware version ARM", - }, - "firmware_version_arm_bootloader": { - "address": 0x0084, - "register_type": "holding", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_length": 1, - "description": "Firmware version ARM Bootloader", - }, - "rtc_datetime": { - "address": 0x0085, - "register_type": "holding", - "data_format": "datetime", - "si_adj": 1, - "signed": False, - "data_length": 6, - "description": "RTC datetime", - }, - "registration_code": { - "address": 0x00AA, - "register_type": "holding", - "data_format": "varchar", - "si_adj": 1, - "signed": False, - "data_length": 6, - "description": "Registration code for external module", - }, - "inverter_power_type": { - "address": 0x00BA, - "register_type": "holding", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_length": 1, - "description": "Inverter power type", - }, - "user_password": { - "address": 0x00E0, - "register_type": "holding", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_length": 1, - "description": "User password", - }, - "advanced_password": { - "address": 0x00E1, - "register_type": "holding", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_length": 1, - "description": "Advanced password", - }, - "machine_type": { - "address": 0x0105, - "register_type": "holding", - "data_format": "uint16", - "si_adj": 1, - "signed": False, - "data_length": 1, - "description": "Machine type: 1:X1, 3:X3", - }, - } - - def get_register_info(self, name: str): - return self.__registers[name] - - def list_register_names(self): - return list(self.__registers.keys()) \ No newline at end of file diff --git a/source/src/solaxx3/rs485.py b/source/src/solaxx3/rs485.py deleted file mode 100644 index 9bbb6f6..0000000 --- a/source/src/solaxx3/rs485.py +++ /dev/null @@ -1,168 +0,0 @@ -from typing import Any - -from pymodbus.client import ModbusSerialClient -from datetime import date, datetime, timedelta -from struct import * -from solaxx3.registers import SolaxRegistersInfo -from time import sleep, perf_counter - - -class SolaxX3: - connected: bool = False - - def __init__( - self, - method="rtu", - port="/dev/ttyUSB0", - baudrate=115200, - timeout=3, - parity="N", - stopbits=1, - bytesize=8, - ) -> None: - self._input_registers_values_list = [] - self._holding_registers_values_list = [] - - self.client = ModbusSerialClient( - method=method, - port=port, - baudrate=baudrate, - timeout=timeout, - parity=parity, - stopbits=stopbits, - bytesize=bytesize, - ) - - def connect(self) -> bool: - self.connected = self.client.connect() - return self.connected - - def _join_msb_lsb(self, msb: int, lsb: int) -> int: - return (msb << 16) | lsb - - def _unsigned16(self, type: str, addr: int, count: int = 1, unit: int = 1) -> int: - - self._input_registers_values_list - if type == "input": - return self._input_registers_values_list[addr] - elif type == "holding": - return self._holding_registers_values_list[addr] - - def _readRegisterRange( - self, type: str, addr: int, count: int = 1, unit: int = 1 - ) -> list: - if type == "input": - return self._input_registers_values_list[addr : addr + count] - elif type == "holding": - return self._holding_registers_values_list[addr : addr + count] - - def _twos_complement(self, number: int, bits: int) -> int: - """ - Compute the 2's complement of the int value val - """ - - # if sign bit is set e.g., 8bit: 128-255 - if (number & (1 << (bits - 1))) != 0: - - # compute negative value - number = number - (1 << bits) - - return number - - def _read_register(self, register_type: str, register_info: dict) -> Any: - """Read the values from a register based on length and sign - - Parameters: - - register_info:dict - dictionary with register definition fields - """ - - if "int" in register_info["data_format"]: - if register_info["data_length"] == 1: - val = self._unsigned16(register_type, register_info["address"]) - - if register_info["data_length"] == 2: - val = self._join_msb_lsb( - self._unsigned16(register_type, register_info["address"] + 1), - self._unsigned16(register_type, register_info["address"]), - ) - - if register_info["signed"]: - val = self._twos_complement(val, register_info["data_length"] * 16) - - val = val / register_info["si_adj"] - - elif "varchar" in register_info["data_format"]: - block = self._readRegisterRange( - register_type, register_info["address"], register_info["data_length"] - ) - sn = [] - for i in range(register_info["data_length"]): - first_byte, second_byte = unpack( - "BB", int.to_bytes(block[i], 2, "little") - ) - if not second_byte == 0x0: - sn.append(chr(second_byte)) - if not first_byte == 0x0: - sn.append(chr(first_byte)) - val = "".join(sn) - - elif "datetime" in register_info["data_format"]: - sec, min, hr, day, mon, year = self._readRegisterRange( - register_type, register_info["address"], register_info["data_length"] - ) - - inverter_datetime = ( - f"{(year+2000):02}-{mon:02}-{day:02} {hr:02}:{min:02}:{sec:02}" - ) - val = datetime.strptime(inverter_datetime, "%Y-%m-%d %H:%M:%S") - - return val - - def read_register(self, register_info: dict) -> tuple: - """Read the values from a register based on length and sign - - Parameters: - - register_info:dict - dictionary with register definition fields - """ - - val = self._read_register(register_info["register_type"], register_info) - - if not "data_unit" in register_info: - return (val, "N/A") - - return (val, register_info["data_unit"]) - - def read(self, name: str): - """Retrieve the value for the register with the provided name""" - - r = SolaxRegistersInfo() - - register_info = r.get_register_info(name) - value = self.read_register(register_info) - - return value - - def list_register_names(self): - r = SolaxRegistersInfo() - return r.list_register_names() - - def read_all_registers(self) -> None: - self._input_registers_values_list = [] - self._holding_registers_values_list = [] - - read_block_length = 100 - for i in range(3): - address = i * read_block_length - values_list = self.client.read_input_registers( - address=address, count=read_block_length, slave=1 - ).registers - self._input_registers_values_list.extend(values_list) - - for i in range(3): - address = i * read_block_length - values_list = self.client.read_holding_registers( - address=address, count=read_block_length, slave=1 - ).registers - self._holding_registers_values_list.extend(values_list) \ No newline at end of file diff --git a/source/src/solaxx3/solax_registers_info.py b/source/src/solaxx3/solax_registers_info.py new file mode 100644 index 0000000..41cdd16 --- /dev/null +++ b/source/src/solaxx3/solax_registers_info.py @@ -0,0 +1,3570 @@ +"""Module holding information about inverter's registers.""" + +from typing import Dict, Union, Literal + +FIELDS = Literal[ + "address", + "register_type", + "data_format", + "si_adj", + "signed", + "data_unit", + "data_length", + "description", +] +FIELD_VALUES = Union[int, str] + + +class SolaxRegistersInfo: + """Class holding information about inverter's registers.""" + + _registers: Dict[str, Dict[FIELDS, FIELD_VALUES]] = { + # input registers + "grid_voltage": { + "address": 0x0000, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "Grid voltage", + }, + "grid_current": { + "address": 0x0001, + "register_type": "input", + "data_format": "int16", + "si_adj": 10, + "signed": True, + "data_unit": "A", + "data_length": 1, + "description": "Grid current", + }, + "grid_power": { + "address": 0x0002, + "register_type": "input", + "data_format": "int16", + "si_adj": 1, + "signed": True, + "data_unit": "W", + "data_length": 1, + "description": "Grid power", + }, + "pv_voltage_1": { + "address": 0x0003, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "PV Voltage 1", + }, + "pv_voltage_2": { + "address": 0x0004, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "PV Voltage 2", + }, + "pv_current_1": { + "address": 0x0005, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "A", + "data_length": 1, + "description": "PV Current 1", + }, + "pv_current_2": { + "address": 0x0006, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "A", + "data_length": 1, + "description": "PV Current 2", + }, + "grid_frequency": { + "address": 0x0007, + "register_type": "input", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_unit": "Hz", + "data_length": 1, + "description": "Grid frequency", + }, + "radiator_temperature": { + "address": 0x0008, + "register_type": "input", + "data_format": "int16", + "si_adj": 1, + "signed": True, + "data_unit": "C", + "data_length": 1, + "description": "Radiator temperature", + }, + "run_mode": { + "address": 0x0009, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": "Inverter status", + }, + "power_dc1": { + "address": 0x000A, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "W", + "data_length": 1, + "description": "DC Power String 1", + }, + "power_dc2": { + "address": 0x000B, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "W", + "data_length": 1, + "description": "DC Power String 2", + }, + "temperature_fault_value": { + "address": 0x000C, + "register_type": "input", + "data_format": "int16", + "si_adj": 1, + "signed": True, + "data_unit": "C", + "data_length": 1, + "description": "Temperature fault value", + }, + "pv1_volt_fault_value": { + "address": 0x000D, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "PV1 volt fault value", + }, + "pv2_volt_fault_value": { + "address": 0x000E, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "PV2 volt fault value", + }, + "gfci_fault_value": { + "address": 0x000F, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1000, + "signed": False, + "data_unit": "A", + "data_length": 1, + "description": "GFCI fault value", + }, + "grid_fault_value": { + "address": 0x0010, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "Grid fault value", + }, + "grid_freq_fault_value_t": { + "address": 0x0011, + "register_type": "input", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_unit": "Hz", + "data_length": 1, + "description": "Grid frequency fault value t", + }, + "dci_fault_value": { + "address": 0x0012, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1000, + "signed": False, + "data_unit": "A", + "data_length": 1, + "description": "DCI fault value", + }, + "time_count_down": { + "address": 0x0013, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1000, + "signed": False, + "data_unit": "second", + "data_length": 1, + "description": "Time remaining until the current operation finishes (e.g. checking)", + }, + "battery_voltage_charge_1": { + "address": 0x0014, + "register_type": "input", + "data_format": "int16", + "si_adj": 10, + "signed": True, + "data_unit": "V", + "data_length": 1, + "description": "Battery voltage charge 1", + }, + "battery_current_charge_1": { + "address": 0x0015, + "register_type": "input", + "data_format": "int16", + "si_adj": 10, + "signed": True, + "data_unit": "A", + "data_length": 1, + "description": "Battery current charge 1", + }, + "battery_power_charge1": { + "address": 0x0016, + "register_type": "input", + "data_format": "int16", + "si_adj": 1, + "signed": True, + "data_unit": "W", + "data_length": 1, + "description": "Battery power charge 1", + }, + "bms_connect_state": { + "address": 0x0017, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": "BMS connect state", + }, + "temperature_battery": { + "address": 0x0018, + "register_type": "input", + "data_format": "int16", + "si_adj": 1, + "signed": True, + "data_unit": "C", + "data_length": 1, + "description": "Temperature of the battery", + }, + "bdc_status": { + "address": 0x0019, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": "BDC Status", + }, + "grid_status": { + "address": 0x001A, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": "Grid Status: 0-OnGrid, 1-OffGrid", + }, + "mppt_count": { + "address": 0x001B, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": "MPPT Count", + }, + "battery_capacity": { + "address": 0x001C, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "%", + "data_length": 1, + "description": "Battery charge percentage", + }, + "output_energy_charge": { + "address": 0x001D, + "register_type": "input", + "data_format": "uint32", + "si_adj": 10, + "signed": False, + "data_unit": "KWh", + "data_length": 2, + "description": "Output energy charge", + }, + "output_energy_charge_today": { + "address": 0x0020, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "KWh", + "data_length": 1, + "description": "Output energy charge today", + }, + "input_energy_charge": { + "address": 0x0021, + "register_type": "input", + "data_format": "uint32", + "si_adj": 10, + "signed": False, + "data_unit": "KWh", + "data_length": 2, + "description": "Input energy charge", + }, + "input_energy_charge_today": { + "address": 0x0023, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "KWh", + "data_length": 1, + "description": "Input energy charge today", + }, + "bms_charge_max_current": { + "address": 0x0024, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "A", + "data_length": 1, + "description": "BMS charge max current", + }, + "bms_discharge_max_current": { + "address": 0x0025, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "A", + "data_length": 1, + "description": "BMS discharge max current", + }, + "bms_battery_capacity": { + "address": 0x0026, + "register_type": "input", + "data_format": "uint32", + "si_adj": 1, + "signed": False, + "data_unit": "Wh", + "data_length": 2, + "description": "BMS Battery Capacity", + }, + "pcs_major_fault": { + "address": 0x003E, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": "PCS Major Fault", + }, + "battery_major_fault": { + "address": 0x003F, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": "Battery Major Fault", + }, + "inv_fault_message": { + "address": 0x0040, + "register_type": "input", + "data_format": "uint32", + "si_adj": 1, + "signed": False, + "data_unit": "N/A", + "data_length": 2, + "description": "Inverter Fault Message: X1:Table 2-4, X2:Table 2-3", + }, + "mgr_fault_message": { + "address": 0x0043, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": "Manager Fault Message: Table 2-5", + }, + "bat_bms_fault_message": { + "address": 0x0044, + "register_type": "input", + "data_format": "uint32", + "si_adj": 1, + "signed": False, + "data_unit": "N/A", + "data_length": 2, + "description": "Battery BMS Fault Message - Table 2-6", + }, + "feed_in_power": { + "address": 0x0046, + "register_type": "input", + "data_format": "int32", + "si_adj": 1, + "signed": True, + "data_unit": "W", + "data_length": 2, + "description": "Feed-in power obtained from Meter or CT", + }, + "energy_to_grid_meter": { + "address": 0x0048, + "register_type": "input", + "data_format": "uint32", + "si_adj": 100, + "signed": False, + "data_unit": "KWh", + "data_length": 2, + "description": "Energy to grid meter value", + }, + "energy_from_grid_meter": { + "address": 0x004A, + "register_type": "input", + "data_format": "uint32", + "si_adj": 100, + "signed": False, + "data_unit": "KWh", + "data_length": 2, + "description": "Energy from grid meter value", + }, + "off_grid_voltage": { + "address": 0x004C, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "Off-Grid Voltage X1", + }, + "off_grid_current": { + "address": 0x004D, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "A", + "data_length": 1, + "description": "Off-Grid Current X1", + }, + "off_grid_power": { + "address": 0x004E, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "VA", + "data_length": 1, + "description": "Off Grid Power X1", + }, + "off_grid_frequency_input_reg": { + "address": 0x004F, + "register_type": "input", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_unit": "Hz", + "data_length": 1, + "description": "Off-Grid Frequency", + }, + "energy_to_grid_today": { + "address": 0x0050, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "KWh", + "data_length": 1, + "description": "The quantity of energy sent to grid today", + }, + "energy_to_grid_total": { + "address": 0x0052, + "register_type": "input", + "data_format": "uint32", + "si_adj": 10, + "signed": False, + "data_unit": "KWh", + "data_length": 2, + "description": "Total energy sent to grid", + }, + "lock_state": { + "address": 0x0054, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": "Lock State", + }, + "bus_volt": { + "address": 0x0066, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "Bus Volt", + }, + "w_dcv_fault_val": { + "address": 0x0067, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "W DCV Fault Value", + }, + "w_overload_fault_val": { + "address": 0x0068, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "W", + "data_length": 1, + "description": "W Overload Fault Value", + }, + "w_battery_volt_fault_val": { + "address": 0x0069, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "W Battery Volt Fault Value", + }, + "grid_voltage_r": { + "address": 0x006A, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "Grid voltage for R phase", + }, + "grid_current_r": { + "address": 0x006B, + "register_type": "input", + "data_format": "int16", + "si_adj": 10, + "signed": True, + "data_unit": "A", + "data_length": 1, + "description": "Grid Current R Phase", + }, + "grid_power_r": { + "address": 0x006C, + "register_type": "input", + "data_format": "int16", + "si_adj": 1, + "signed": True, + "data_unit": "W", + "data_length": 1, + "description": "Grid Power R Phase", + }, + "grid_frequency_r": { + "address": 0x006D, + "register_type": "input", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_unit": "Hz", + "data_length": 1, + "description": "Grid Frequency R Phase", + }, + "grid_voltage_s": { + "address": 0x006E, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "Grid voltage for S phase", + }, + "grid_current_s": { + "address": 0x006F, + "register_type": "input", + "data_format": "int16", + "si_adj": 10, + "signed": True, + "data_unit": "A", + "data_length": 1, + "description": "Grid Current S Phase", + }, + "grid_power_s": { + "address": 0x0070, + "register_type": "input", + "data_format": "int16", + "si_adj": 1, + "signed": True, + "data_unit": "W", + "data_length": 1, + "description": "Grid Power S Phase", + }, + "grid_frequency_s": { + "address": 0x0071, + "register_type": "input", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_unit": "Hz", + "data_length": 1, + "description": "Grid Frequency S", + }, + "grid_voltage_t": { + "address": 0x0072, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "Grid voltage for T phase", + }, + "grid_current_t": { + "address": 0x0073, + "register_type": "input", + "data_format": "int16", + "si_adj": 10, + "signed": True, + "data_unit": "A", + "data_length": 1, + "description": "Grid Current T Phase", + }, + "grid_power_t": { + "address": 0x0074, + "register_type": "input", + "data_format": "int16", + "si_adj": 1, + "signed": True, + "data_unit": "W", + "data_length": 1, + "description": "Grid Power T Phase", + }, + "grid_frequency_t": { + "address": 0x0075, + "register_type": "input", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_unit": "Hz", + "data_length": 1, + "description": "Grid Frequency T Phase", + }, + "off_grid_volt_r": { + "address": 0x0076, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "Off-Grid Volt R Phase", + }, + "off_grid_current_r": { + "address": 0x0077, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "A", + "data_length": 1, + "description": "Off-Grid Current R Phase", + }, + "off_grid_power_active_r": { + "address": 0x0078, + "register_type": "input", + "data_format": "int16", + "si_adj": 1, + "signed": True, + "data_unit": "W", + "data_length": 1, + "description": "Off-Grid Active Power R Phase", + }, + "off_grid_power_s_r": { + "address": 0x0079, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "VA", + "data_length": 1, + "description": "Off-Grid S Power R Phase", + }, + "off_grid_volt_s": { + "address": 0x007A, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "Off-Grid Volt S Phase", + }, + "off_grid_current_s": { + "address": 0x007B, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "A", + "data_length": 1, + "description": "Off-Grid Current S Phase", + }, + "off_grid_power_active_s": { + "address": 0x007C, + "register_type": "input", + "data_format": "int16", + "si_adj": 1, + "signed": True, + "data_unit": "W", + "data_length": 1, + "description": "Off-Grid Active Power S Phase", + }, + "off_grid_power_s_s": { + "address": 0x007D, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "VA", + "data_length": 1, + "description": "Off-Grid S Power S Phase", + }, + "off_grid_volt_t": { + "address": 0x007E, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "Off-Grid Volt T Phase", + }, + "off_grid_current_t": { + "address": 0x007F, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "A", + "data_length": 1, + "description": "Off-Grid Current T Phase", + }, + "off_grid_power_active_t": { + "address": 0x0080, + "register_type": "input", + "data_format": "int16", + "si_adj": 1, + "signed": True, + "data_unit": "W", + "data_length": 1, + "description": "Off-Grid Active Power T Phase", + }, + "off_grid_power_s_t": { + "address": 0x0081, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "VA", + "data_length": 1, + "description": "Off-Grid S Power T Phase", + }, + "feedin_power_r_phase": { + "address": 0x0082, + "register_type": "input", + "data_format": "int32", + "si_adj": 1, + "signed": True, + "data_unit": "W", + "data_length": 2, + "description": "Feedin Power R Phase", + }, + "feedin_power_s_phase": { + "address": 0x0084, + "register_type": "input", + "data_format": "int32", + "si_adj": 1, + "signed": True, + "data_unit": "W", + "data_length": 2, + "description": "Feedin Power S Phase", + }, + "feedin_power_t_phase": { + "address": 0x0086, + "register_type": "input", + "data_format": "int32", + "si_adj": 1, + "signed": True, + "data_unit": "W", + "data_length": 2, + "description": "Feedin Power T Phase", + }, + "ongrid_run_time": { + "address": 0x0088, + "register_type": "input", + "data_format": "int32", + "si_adj": 10, + "signed": True, + "data_unit": "hour", + "data_length": 2, + "description": "On-Grid Run Time", + }, + "offgrid_run_time": { + "address": 0x008A, + "register_type": "input", + "data_format": "int32", + "si_adj": 10, + "signed": True, + "data_unit": "hour", + "data_length": 2, + "description": "Off-Grid Run Time", + }, + "offgrid_yield_total": { + "address": 0x008E, + "register_type": "input", + "data_format": "uint32", + "si_adj": 10, + "signed": False, + "data_unit": "KWh", + "data_length": 2, + "description": "Off-Grid Yield Total", + }, + "offgrid_yield_today": { + "address": 0x0090, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "KWh", + "data_length": 1, + "description": "Off-Grid Yield Today", + }, + "echarge_today": { + "address": 0x0091, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "KWh", + "data_length": 1, + "description": "Echarge energy today", + }, + "echarge_total": { + "address": 0x0092, + "register_type": "input", + "data_format": "uint32", + "si_adj": 10, + "signed": False, + "data_unit": "KWh", + "data_length": 2, + "description": "Echarge Total", + }, + "solar_energy_total": { + "address": 0x0094, + "register_type": "input", + "data_format": "uint32", + "si_adj": 10, + "signed": False, + "data_unit": "KWh", + "data_length": 2, + "description": "Total solar energy", + }, + "solar_energy_today": { + "address": 0x0096, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "KWh", + "data_length": 1, + "description": "Solar energy today", + }, + "feed_in_energy_today": { + "address": 0x0098, + "register_type": "input", + "data_format": "uint32", + "si_adj": 100, + "signed": False, + "data_unit": "KWh", + "data_length": 2, + "description": "Energy to grid meter for today", + }, + "consumption_energy_today": { + "address": 0x009A, + "register_type": "input", + "data_format": "uint32", + "si_adj": 100, + "signed": False, + "data_unit": "KWh", + "data_length": 2, + "description": "Energy from grid meter for today", + }, + "inv_volt_r": { + "address": 0x009C, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "Inverter Volt R Phase", + }, + "inv_volt_s": { + "address": 0x009D, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "Inverter Volt S Phase", + }, + "inv_volt_t": { + "address": 0x009E, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "Inverter Volt T Phase", + }, + "feedin_power_meter_2": { + "address": 0x00A8, + "register_type": "input", + "data_format": "int32", + "si_adj": 1, + "signed": True, + "data_unit": "W", + "data_length": 2, + "description": "Power to Grid Meter 2", + }, + "feedin_energy_total_meter_2": { + "address": 0x00AA, + "register_type": "input", + "data_format": "uint32", + "si_adj": 100, + "signed": False, + "data_unit": "KWh", + "data_length": 2, + "description": "Energy to the grid", + }, + "consum_energy_total_meter_2": { + "address": 0x00AC, + "register_type": "input", + "data_format": "uint32", + "si_adj": 100, + "signed": False, + "data_unit": "KWh", + "data_length": 2, + "description": "Energy from grid", + }, + "feedin_energy_today_meter_2": { + "address": 0x00AE, + "register_type": "input", + "data_format": "uint32", + "si_adj": 100, + "signed": False, + "data_unit": "KWh", + "data_length": 2, + "description": "Energy to grid", + }, + "consum_energy_today_meter_2": { + "address": 0x00B0, + "register_type": "input", + "data_format": "uint32", + "si_adj": 100, + "signed": False, + "data_unit": "KWh", + "data_length": 2, + "description": "Energy from grid", + }, + "feedin_power_r_phase_meter_2": { + "address": 0x00B2, + "register_type": "input", + "data_format": "int32", + "si_adj": 1, + "signed": True, + "data_unit": "W", + "data_length": 2, + "description": "Feedin Power R Phase Meter 2", + }, + "feedin_power_s_phase_meter_2": { + "address": 0x00B4, + "register_type": "input", + "data_format": "int32", + "si_adj": 1, + "signed": True, + "data_unit": "W", + "data_length": 2, + "description": "Feedin Power S Phase Meter 2", + }, + "feedin_power_t_phase_meter_2": { + "address": 0x00B6, + "register_type": "input", + "data_format": "int32", + "si_adj": 1, + "signed": True, + "data_unit": "W", + "data_length": 2, + "description": "Feedin Power T Phase Meter 2", + }, + "meter_1_communication_state": { + "address": 0x00B8, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": "Meter 1 Communication State: 0:Com Error, 1:Normal", + }, + "meter_2_communication_state": { + "address": 0x00B9, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": "Meter 2 Communication State: 0:Com Error, 1:Normal", + }, + "battery_tem_high": { + "address": 0x00BA, + "register_type": "input", + "data_format": "int16", + "si_adj": 10, + "signed": True, + "data_unit": "C", + "data_length": 1, + "description": "Battery Temperature High", + }, + "battery_tem_low": { + "address": 0x00BB, + "register_type": "input", + "data_format": "int16", + "si_adj": 10, + "signed": True, + "data_unit": "C", + "data_length": 1, + "description": "Battery Temperature Low", + }, + "cell_voltage_high": { + "address": 0x00BC, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1000, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "Cell Voltage High", + }, + "cell_voltage_low": { + "address": 0x00BD, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1000, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "Cell Voltage Low", + }, + "bms_user_soc": { + "address": 0x00BE, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "%", + "data_length": 1, + "description": "BMS User SOC", + }, + "bms_user_soh": { + "address": 0x00BF, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "%", + "data_length": 1, + "description": "BMS User SOH", + }, + "grid_reactive_power_total_meter": { + "address": 0x00C0, + "register_type": "input", + "data_format": "int16", + "si_adj": 1, + "signed": True, + "data_unit": "Var", + "data_length": 1, + "description": "Grid Reactive Power Total Meter", + }, + "grid_reactive_power_r_meter": { + "address": 0x00C1, + "register_type": "input", + "data_format": "int16", + "si_adj": 1, + "signed": True, + "data_unit": "Var", + "data_length": 1, + "description": "Grid Reactive Power Phase R Meter", + }, + "grid_reactive_power_s_meter": { + "address": 0x00C2, + "register_type": "input", + "data_format": "int16", + "si_adj": 1, + "signed": True, + "data_unit": "Var", + "data_length": 1, + "description": "Grid Reactive Power Phase S Meter", + }, + "grid_reactive_power_t_meter": { + "address": 0x00C3, + "register_type": "input", + "data_format": "int16", + "si_adj": 1, + "signed": True, + "data_unit": "Var", + "data_length": 1, + "description": "Grid Reactive Power Phase T Meter", + }, + "grid_power_factor_total_meter": { + "address": 0x00C4, + "register_type": "input", + "data_format": "int16", + "si_adj": 100, + "signed": True, + "data_unit": "N/A", + "data_length": 1, + "description": "Grid Power Factor Total Meter", + }, + "grid_power_factor_r_meter": { + "address": 0x00C5, + "register_type": "input", + "data_format": "int16", + "si_adj": 100, + "signed": True, + "data_unit": "N/A", + "data_length": 1, + "description": "Grid Power Factor Phase R Meter", + }, + "grid_power_factor_s_meter": { + "address": 0x00C6, + "register_type": "input", + "data_format": "int16", + "si_adj": 100, + "signed": True, + "data_unit": "N/A", + "data_length": 1, + "description": "Grid Power Factor Phase S Meter", + }, + "grid_power_factor_t_meter": { + "address": 0x00C7, + "register_type": "input", + "data_format": "int16", + "si_adj": 100, + "signed": True, + "data_unit": "N/A", + "data_length": 1, + "description": "Grid Power Factor Phase T Meter", + }, + "grid_frequency_meter": { + "address": 0x00C8, + "register_type": "input", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_unit": "Hz", + "data_length": 1, + "description": "Grid Frequency Meter", + }, + "grid_voltage_total_meter": { + "address": 0x00C9, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "Grid Voltage Total Meter", + }, + "grid_voltage_r_meter": { + "address": 0x00CA, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "Grid Voltage R Meter", + }, + "grid_voltage_s_meter": { + "address": 0x00CB, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "Grid Voltage S Meter", + }, + "grid_voltage_t_meter": { + "address": 0x00CC, + "register_type": "input", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "Grid Voltage T Meter", + }, + "grid_current_total_meter": { + "address": 0x00CD, + "register_type": "input", + "data_format": "int16", + "si_adj": 10, + "signed": True, + "data_unit": "A", + "data_length": 1, + "description": "Grid Current Total Meter", + }, + "grid_current_r_meter": { + "address": 0x00CE, + "register_type": "input", + "data_format": "int16", + "si_adj": 10, + "signed": True, + "data_unit": "A", + "data_length": 1, + "description": "Grid Current R Meter", + }, + "grid_current_s_meter": { + "address": 0x00CF, + "register_type": "input", + "data_format": "int16", + "si_adj": 10, + "signed": True, + "data_unit": "A", + "data_length": 1, + "description": "Grid Current S Meter", + }, + "grid_current_t_meter": { + "address": 0x00D0, + "register_type": "input", + "data_format": "int16", + "si_adj": 10, + "signed": True, + "data_unit": "A", + "data_length": 1, + "description": "Grid Current T Meter", + }, + "modbus_power_control": { + "address": 0x0100, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": """Modbus Power Control + 0:disable remote contro + 1:enable power control + 2:enable electric quality control + 3:enable SOC target control""", + }, + "target_finish_tag": { + "address": 0x0101, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": "Target Finish Tag: 0:unfinished, 1:finished", + }, + "active_power_target": { + "address": 0x0102, + "register_type": "input", + "data_format": "int32", + "si_adj": 1, + "signed": True, + "data_unit": "W", + "data_length": 2, + "description": "Active Power Target", + }, + "w_reactive_power_target": { + "address": 0x0104, + "register_type": "input", + "data_format": "int32", + "si_adj": 1, + "signed": True, + "data_unit": "Var", + "data_length": 2, + "description": "W Reactive Power Target", + }, + "w_active_power_real": { + "address": 0x0106, + "register_type": "input", + "data_format": "int32", + "si_adj": 1, + "signed": True, + "data_unit": "W", + "data_length": 2, + "description": "W Active Power Real", + }, + "w_reactive_power_real": { + "address": 0x0108, + "register_type": "input", + "data_format": "int32", + "si_adj": 1, + "signed": True, + "data_unit": "Var", + "data_length": 2, + "description": "W Reactive Power Real", + }, + "w_active_power_upper": { + "address": 0x010A, + "register_type": "input", + "data_format": "int32", + "si_adj": 1, + "signed": True, + "data_unit": "W", + "data_length": 2, + "description": "W Active Power Upper", + }, + "w_active_power_lower": { + "address": 0x010C, + "register_type": "input", + "data_format": "int32", + "si_adj": 1, + "signed": True, + "data_unit": "W", + "data_length": 2, + "description": "W Active Power Lower", + }, + "w_reactive_power_upper": { + "address": 0x010E, + "register_type": "input", + "data_format": "int32", + "si_adj": 1, + "signed": True, + "data_unit": "Var", + "data_length": 2, + "description": "W Reactive Power Upper", + }, + "w_reactive_power_lower": { + "address": 0x0110, + "register_type": "input", + "data_format": "int32", + "si_adj": 1, + "signed": True, + "data_unit": "Var", + "data_length": 2, + "description": "W Reactive Power Lower", + }, + "target_energy": { + "address": 0x0112, + "register_type": "input", + "data_format": "int32", + "si_adj": 1, + "signed": True, + "data_unit": "Wh", + "data_length": 2, + "description": "Target Energy", + }, + "charge_discharge_power": { + "address": 0x0114, + "register_type": "input", + "data_format": "int32", + "si_adj": 1, + "signed": True, + "data_unit": "W", + "data_length": 2, + "description": "Charge Discharge Power", + }, + "chargeable_electric_capacity": { + "address": 0x0116, + "register_type": "input", + "data_format": "uint32", + "si_adj": 1, + "signed": False, + "data_unit": "Wh", + "data_length": 2, + "description": "Chargeable Electric Capacity", + }, + "dischargeable_electric_capacity": { + "address": 0x0118, + "register_type": "input", + "data_format": "uint32", + "si_adj": 1, + "signed": False, + "data_unit": "Wh", + "data_length": 2, + "description": "Dischargeable Electric Capacity", + }, + "time_of_duration": { + "address": 0x011A, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "second", + "data_length": 1, + "description": "time_of_duration", + }, + "target_soc": { + "address": 0x011B, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "%", + "data_length": 1, + "description": "Target SOC", + }, + "soc_upper": { + "address": 0x011C, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "%", + "data_length": 1, + "description": "SOC Upper", + }, + "soc_lower": { + "address": 0x011D, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "%", + "data_length": 1, + "description": "SOC Lower", + }, + "remote_control_timeout": { + "address": 0x011E, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "second", + "data_length": 1, + "description": "Remote Control Timeout: 4-65535", + }, + "w_battery_force_charge_flag": { + "address": 0x011F, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": "W Battery Force Charge Flag: 0:No Action, 1:Force Charge", + }, + "w_bms_relay_state": { + "address": 0x0120, + "register_type": "input", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": "W BMS Relay State: 0:OFF, 1:ON", + }, + # holding registers + "serial_number": { + "address": 0x0000, + "register_type": "holding", + "data_format": "varchar", + "si_adj": 1, + "signed": False, + "data_unit": "N/A", + "data_length": 7, + "description": "Inverter' serial number", + }, + "factory_name": { + "address": 0x0007, + "register_type": "holding", + "data_format": "varchar", + "si_adj": 1, + "signed": False, + "data_unit": "N/A", + "data_length": 7, + "description": "Factory name", + }, + "module_name": { + "address": 0x000E, + "register_type": "holding", + "data_format": "varchar", + "si_adj": 1, + "signed": False, + "data_unit": "N/A", + "data_length": 7, + "description": "Module name", + }, + "start_time": { + "address": 0x0016, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "second", + "data_length": 1, + "description": "Launch wait time", + }, + "reconnection_time": { + "address": 0x0017, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "second", + "data_length": 1, + "description": "Reconnection time", + }, + "checking_time": { + "address": 0x0018, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "second", + "data_length": 1, + "description": "Checking time", + }, + "vac_min_protect": { + "address": 0x0019, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "Allowed minimum grid voltage", + }, + "vac_max_protect": { + "address": 0x001A, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "Allowed maximum grid voltage", + }, + "fac_min_protect": { + "address": 0x001B, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_unit": "Hz", + "data_length": 1, + "description": "Allowed minimum grid frequency", + }, + "fac_max_protect": { + "address": 0x001C, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_unit": "Hz", + "data_length": 1, + "description": "Allowed maximum grid frequency", + }, + "safety_code": { + "address": 0x001D, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": "Safety type", + }, + "mate_box_enable": { + "address": 0x001E, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": "0:Disable 1:Enable", + }, + "grid_10_min_avg_protect": { + "address": 0x001F, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "10minutes over voltage protect", + }, + "vac_min_slow_protect": { + "address": 0x0020, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "Grid undervoltage protect value", + }, + "vac_max_slow_protect": { + "address": 0x0021, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "Grid overvoltage protect value", + }, + "fac_min_slow_protect": { + "address": 0x0022, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_unit": "Hz", + "data_length": 1, + "description": "Grid underfrequency protect value", + }, + "fac_max_slow_protect": { + "address": 0x0023, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_unit": "Hz", + "data_length": 1, + "description": "Grid overfrequency protect value", + }, + "power_limits_percent": { + "address": 0x0025, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "%", + "data_length": 1, + "description": "Output power limits precent", + }, + "powerfactor_mode": { + "address": 0x0026, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": "0:Off 1:Over Excited 2:Under Excited 3:Curve 4:Qu 5:Fix Q Power", + }, + "powerfactor_data": { + "address": 0x0027, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": "Power factor data", + }, + "power_factor_curve_pf1": { + "address": 0x0028, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": "PowerFactor_Curve_PF1", + }, + "power_factor_curve_pf2": { + "address": 0x0029, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": "PowerFactor_Curve_PF2", + }, + "power_factor_curve_pf3": { + "address": 0x002A, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": "PowerFactor_Curve_PF3", + }, + "power_factor_curve_pf4": { + "address": 0x002B, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": "PowerFactor_Curve_PF4", + }, + "power_factor_curve_power1": { + "address": 0x002C, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "%", + "data_length": 1, + "description": "PowerFactor_Curve_Power1", + }, + "power_factor_curve_power2": { + "address": 0x002D, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "%", + "data_length": 1, + "description": "PowerFactor_Curve_Power2", + }, + "power_factor_curve_power3": { + "address": 0x002E, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "%", + "data_length": 1, + "description": "PowerFactor_Curve_Power3", + }, + "power_factor_curve_power4": { + "address": 0x002F, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "%", + "data_length": 1, + "description": "PowerFactor_Curve_Power4", + }, + "power_factor_curve_pf_lock_in_point": { + "address": 0x0030, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": "PowerFactor_Curve_PfLockInPoint", + }, + "power_factor_curve_pf_lock_out_point": { + "address": 0x0031, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": "PowerFactor_Curve_PfLockOutPoint", + }, + "power_factor_curve_3_tau": { + "address": 0x0032, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "second", + "data_length": 1, + "description": "PowerFactor_Curve_3Tau", + }, + "power_factor_qu_volt_ratio_1": { + "address": 0x0033, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "%", + "data_length": 1, + "description": "PowerFactor_Qu_VoltRatio1", + }, + "power_factor_qu_volt_ratio_4": { + "address": 0x0034, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "%", + "data_length": 1, + "description": "PowerFactor_Qu_VoltRatio4", + }, + "power_factor_qu_qu_response_v1": { + "address": 0x0035, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "PowerFactor_Qu_QuResponseV1", + }, + "power_factor_qu_qu_response_v2": { + "address": 0x0036, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "PowerFactor_Qu_QuResponseV2", + }, + "power_factor_qu_qu_response_v3": { + "address": 0x0037, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "PowerFactor_Qu_QuResponseV3", + }, + "power_factor_qu_qu_response_v4": { + "address": 0x0038, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_unit": "V", + "data_length": 1, + "description": "PowerFactor_Qu_QuResponseV4", + }, + "power_factor_qu_k": { + "address": 0x0039, + "register_type": "holding", + "data_format": "int16", + "si_adj": 10, + "signed": True, + "data_unit": "N/A", + "data_length": 1, + "description": "PowerFactor_Qu_K", + }, + "power_factor_qu_3_tau": { + "address": 0x003A, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "second", + "data_length": 1, + "description": "PowerFactor_Qu_3Tau", + }, + "power_factor_qu_qu_delay_timer": { + "address": 0x003B, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "second", + "data_length": 1, + "description": "PowerFactor_Qu_QuDelayTimer", + }, + "power_factor_qu_qu_lock_enable": { + "address": 0x003C, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "N/A", + "data_length": 1, + "description": "PowerFactor_Qu_QuLockEn; 0:Disable 1:Enable", + }, + "power_factor_qu_qu_lock_in": { + "address": 0x003D, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "%", + "data_length": 1, + "description": "PowerFactor_Qu_QuLockIn", + }, + "power_factor_qu_qu_lock_out": { + "address": 0x003E, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_unit": "%", + "data_length": 1, + "description": "PowerFactor_Qu_QuLockOut", + }, + "power_factor_fix_q_power": { + "address": 0x003F, + "register_type": "holding", + "data_format": "int16", + "si_adj": 1, + "signed": True, + "data_unit": "Var", + "data_length": 1, + "description": "PowerFactor_FixQPower", + }, + "power_factor_fix_q_power_max": { + "address": 0x0040, + "register_type": "holding", + "data_format": "int16", + "si_adj": 1, + "signed": True, + "data_unit": "Var", + "data_length": 1, + "description": "PowerFactor_FixQPower_Max", + }, + "power_factor_fix_q_power_min": { + "address": 0x0041, + "register_type": "holding", + "data_format": "int16", + "si_adj": 1, + "signed": True, + "data_unit": "Var", + "data_length": 1, + "description": "PowerFactor_FixQPower_Min", + }, + "w_connection_fl": { + "address": 0x0042, + "register_type": "holding", + "data_format": "int16", + "si_adj": 100, + "signed": True, + "data_unit": "Hz", + "data_length": 1, + "description": "Connection Low frequency", + }, + "w_connection_fh": { + "address": 0x0043, + "register_type": "holding", + "data_format": "int16", + "si_adj": 100, + "signed": True, + "data_unit": "Hz", + "data_length": 1, + "description": "Connection High frequency", + }, + "w_connection_vl": { + "address": 0x0044, + "register_type": "holding", + "data_format": "int16", + "si_adj": 10, + "signed": True, + "data_unit": "V", + "data_length": 1, + "description": "Connection Low voltage", + }, + "w_connection_vh": { + "address": 0x0045, + "register_type": "holding", + "data_format": "int16", + "si_adj": 10, + "signed": True, + "data_unit": "V", + "data_length": 1, + "description": "Connection High voltage", + }, + "w_connection_observe_t": { + "address": 0x0046, + "register_type": "holding", + "data_format": "int16", + "si_adj": 1, + "signed": True, + "data_unit": "second", + "data_length": 1, + "description": "Connection Observation time", + }, + "w_connection_gradient_enable": { + "address": 0x0047, + "register_type": "holding", + "data_format": "int16", + "si_adj": 1, + "signed": True, + "data_unit": "N/A", + "data_length": 1, + "description": "Connection Gradient Select", + }, + "w_reconnection_fl": { + "address": 0x0048, + "register_type": "holding", + "data_format": "int16", + "si_adj": 100, + "signed": True, + "data_unit": "Hz", + "data_length": 1, + "description": "Reconnection Low frequency", + }, + "w_reconnection_fh": { + "address": 0x0049, + "register_type": "holding", + "data_format": "int16", + "si_adj": 100, + "signed": True, + "data_unit": "Hz", + "data_length": 1, + "description": "Reconnection High frequency", + }, + "w_reconnection_vl": { + "address": 0x004A, + "register_type": "holding", + "data_format": "int16", + "si_adj": 10, + "signed": True, + "data_unit": "V", + "data_length": 1, + "description": "Reconnection Low voltage", + }, + "w_reconnection_vh": { + "address": 0x004B, + "register_type": "holding", + "data_format": "int16", + "si_adj": 10, + "signed": True, + "data_unit": "V", + "data_length": 1, + "description": "Reconnection High voltage", + }, + "w_reconnection_observe_t": { + "address": 0x004C, + "register_type": "holding", + "data_format": "int16", + "si_adj": 1, + "signed": True, + "data_unit": "second", + "data_length": 1, + "description": "Reconnection Observation time", + }, + "w_reconnection_gradient_enable": { + "address": 0x004D, + "register_type": "holding", + "data_format": "int16", + "si_adj": 1, + "signed": True, + "data_unit": "N/A", + "data_length": 1, + "description": "Reconnection Gradient Select", + }, + "w_reconnection_gradient": { + "address": 0x004E, + "register_type": "holding", + "data_format": "int16", + "si_adj": 1, + "signed": True, + "data_unit": "%", + "data_length": 1, + "description": "Reconnection Gradient", + }, + "firmware_version_dsp_minor": { + "address": 0x007D, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Firmware version DSP Minor", + }, + "hardware_version_dsp": { + "address": 0x007E, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Hardware version DSP", + }, + "firmware_version_dsp_major": { + "address": 0x007F, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Firmware version DSP major", + }, + "firmware_version_arm_major": { + "address": 0x0080, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Firmware version ARM major", + }, + "firmware_version_modbus_rtu": { + "address": 0x0082, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Firmware version MODBUS RTU", + }, + "firmware_version_arm_minor": { + "address": 0x0083, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Firmware version ARM minor", + }, + "firmware_version_arm_bootloader": { + "address": 0x0084, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Firmware version ARM Bootloader", + }, + "rtc_datetime": { + "address": 0x0085, + "register_type": "holding", + "data_format": "datetime", + "si_adj": 1, + "signed": False, + "data_length": 6, + "data_unit": "N/A", + "description": "RTC datetime", + }, + "solar_charger_use_mode": { + "address": 0x008B, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Solar Charger Use Mode", + }, + "manual_mode": { + "address": 0x008C, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Manual Mode", + }, + "w_battery1_type": { + "address": 0x008D, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "wBattery1 Type; 0:Lead Acid 1:Lithium", + }, + "charge_float_volt": { + "address": 0x008E, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_length": 1, + "data_unit": "V", + "description": "Lead-acid battery charge_float voltage", + }, + "battery_discharge_cut_voltage": { + "address": 0x008F, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_length": 1, + "data_unit": "V", + "description": "Lead-acid battery discharge cut-off voltage", + }, + "battery_charge_max_current": { + "address": 0x0090, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_length": 1, + "data_unit": "A", + "description": "Lead-acid battery charge maximum current", + }, + "battery_discharge_max_current": { + "address": 0x0091, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_length": 1, + "data_unit": "A", + "description": "Lead-acid battery discharge maximum Current", + }, + "absorpt_voltage": { + "address": 0x0092, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_length": 1, + "data_unit": "V", + "description": "Lead-acid battery absorpt voltage", + }, + "self_use_night_charge_upper_soc": { + "address": 0x0094, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "%", + "description": "This value will be enabled if SelfUse_NightCharge_Enable is 1.", + }, + "set_charge_and_discharge_period_2_enable": { + "address": 0x009B, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Whether to use period 2.", + }, + "eps_restart_soc": { + "address": 0x00A0, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "%", + "description": "Eps restart soc; 10-100", + }, + "hot_standby_enable": { + "address": 0x00A1, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Hot Standby EN; 0:Disable 1:Enable", + }, + "extend_bms_setting": { + "address": 0x00A2, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Extend Bms Setting; 0:Disable 1:Enable", + }, + "battery_heating_enable": { + "address": 0x00A3, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Battery Heating En; 0:Disable 1:Enable", + }, + "w_battery_discharge_backup_voltage": { + "address": 0x00A8, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_length": 1, + "data_unit": "V", + "description": "wBattery Discharge Backup Voltage", + }, + "match_resistance_set": { + "address": 0x00A9, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "(X3) match resistance set; 0:Disable 1:Enable", + }, + "registration_code": { + "address": 0x00AA, + "register_type": "holding", + "data_format": "varchar", + "si_adj": 1, + "signed": False, + "data_length": 5, + "data_unit": "N/A", + "description": "Registration code for external module", + }, + "modbus_rtu_address": { + "address": 0x00AF, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Modbus RTU Address", + }, + "modbus_rtu_braud_rate": { + "address": 0x00B0, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Modbus RTU braud rate; 0:115200 1:57600 2:56000 3:38400 4:19200 5:14400 6:9600", + }, + "eps_bat_low_auto_recover_voltage": { + "address": 0x00B1, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_length": 1, + "data_unit": "V", + "description": "Eps Bat Low Auto Recover Voltage", + }, + "p_grid_bias": { + "address": 0x00B2, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "P grid Bias: 0:Disable 1:Grid 2:INV", + }, + "factory_limit": { + "address": 0x00B5, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "W", + "description": "Factory limit", + }, + "export_control_user_limit": { + "address": 0x00B6, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "W", + "description": "export control user limit", + }, + "off_grid_mute": { + "address": 0x00B7, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Off-grid mute: 0(off)/1(on)", + }, + "off_grid_min_soc": { + "address": 0x00B8, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "%", + "description": "Off-grid Min SoC", + }, + "off_grid_frequency_holding_reg": { + "address": 0x00B9, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Off-grid Frequency", + }, + "inverter_power_type": { + "address": 0x00BA, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "W", + "description": "Inverter power type", + }, + "language": { + "address": 0x00BB, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Language", + }, + "enable_mppt": { + "address": 0x00BC, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Enable MPPT", + }, + "wtuvp_l2": { + "address": 0x00BD, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1000, + "signed": False, + "data_length": 1, + "data_unit": "second", + "description": "wTuvp L2", + }, + "wtovp_l2": { + "address": 0x00BE, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1000, + "signed": False, + "data_length": 1, + "data_unit": "second", + "description": "wTovp L2", + }, + "wtufp_l2": { + "address": 0x00BF, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1000, + "signed": False, + "data_length": 1, + "data_unit": "second", + "description": "wTufp L2", + }, + "wtofp_l2": { + "address": 0x00C0, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1000, + "signed": False, + "data_length": 1, + "data_unit": "second", + "description": "wTofp L2", + }, + "wtuvp_l1": { + "address": 0x00C1, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1000, + "signed": False, + "data_length": 1, + "data_unit": "second", + "description": "wTuvp L1", + }, + "wtovp_l1": { + "address": 0x00C2, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1000, + "signed": False, + "data_length": 1, + "data_unit": "second", + "description": "wTovp L1", + }, + "wtufp_l1": { + "address": 0x00C3, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1000, + "signed": False, + "data_length": 1, + "data_unit": "second", + "description": "wTufp L1", + }, + "wtofp_l1": { + "address": 0x00C4, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1000, + "signed": False, + "data_length": 1, + "data_unit": "second", + "description": "wTofp L1", + }, + "test_step": { + "address": 0x00C5, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Test Step", + }, + "ovp_value_ovp_59_s2": { + "address": 0x00C6, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_length": 1, + "data_unit": "V", + "description": "OvpValue(Ovp(59.S2))", + }, + "ovp_time_ovp_59_s2": { + "address": 0x00C7, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1000, + "signed": False, + "data_length": 1, + "data_unit": "second", + "description": "OvpTime(Ovp(59.S2))", + }, + "uvp_value_uvp_27_s1": { + "address": 0x00C8, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_length": 1, + "data_unit": "V", + "description": "UvpValue(Uvp(27.S1))", + }, + "uvp_time_uvp_27_s1": { + "address": 0x00C9, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1000, + "signed": False, + "data_length": 1, + "data_unit": "second", + "description": "UvpTime(Uvp(27.S1))", + }, + "ofp_value_ofp_81_s1": { + "address": 0x00CA, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_length": 1, + "data_unit": "Hz", + "description": "OfpValue(Ofp(81>.S1))", + }, + "ofp_time_ofp_81_s1": { + "address": 0x00CB, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1000, + "signed": False, + "data_length": 1, + "data_unit": "second", + "description": "OfpTime(Ofp(81>.S1))", + }, + "ufp_value_ufp_81_s1": { + "address": 0x00CC, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_length": 1, + "data_unit": "Hz", + "description": "UfpValue(Ufp(81<.S1))", + }, + "ufp_time_ufp_81_s1": { + "address": 0x00CD, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1000, + "signed": False, + "data_length": 1, + "data_unit": "second", + "description": "UfpTime(Ufp(81<.S1))", + }, + "self_test_ovp_10m_avg_val": { + "address": 0x00CE, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_length": 1, + "data_unit": "V", + "description": "Self Test Ovp 10m Avg Val (Ovp_10(59.S1))", + }, + "self_test_ovp_10m_avg_time": { + "address": 0x00CF, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "second", + "description": "Self Test Ovp 10m Avg Time (Ovp_10(59.S1))", + }, + "self_test_ofp_val_restrictive": { + "address": 0x00D0, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_length": 1, + "data_unit": "Hz", + "description": "Self Test Ofp Val Restrictive (Ofp2(81>.S2))", + }, + "self_test_ofp_time_restrictive": { + "address": 0x00D1, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1000, + "signed": False, + "data_length": 1, + "data_unit": "second", + "description": "Self Test Ofp Time Restrictive (Ofp2(81>.S2))", + }, + "self_test_ufp_val_restrictive": { + "address": 0x00D2, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_length": 1, + "data_unit": "Hz", + "description": "Self Test Ufp Val Restrictive (Ufp2(81<.S2))", + }, + "self_test_ufp_time_restrictive": { + "address": 0x00D3, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1000, + "signed": False, + "data_length": 1, + "data_unit": "second", + "description": "Self Test Ufp Time Restrictive (Ufp2(81<.S2))", + }, + "self_test_uvp_restrictive_val": { + "address": 0x00D4, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_length": 1, + "data_unit": "V", + "description": "Self Test Uvp Restrictive Val (Uvp(27.S2))", + }, + "self_test_uvp_restrictive_time": { + "address": 0x00D5, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1000, + "signed": False, + "data_length": 1, + "data_unit": "second", + "description": "Self Test Uvp Restrictive Time (Uvp(27.S2))", + }, + "self_test_time": { + "address": 0x00D6, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "second", + "description": "Self Test Time", + }, + "main_breaker_current_limit": { + "address": 0x00D7, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "A", + "description": "Main Breaker Current Limit", + }, + "w_inverter_output_switch": { + "address": 0x00DA, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "w Inverter OutPut Switch", + }, + "ofpl_point": { + "address": 0x00DB, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_length": 1, + "data_unit": "Hz", + "description": "Overfrequency load reduction point", + }, + "ofpl_set_rate": { + "address": 0x00DC, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "%", + "description": "Overfrequency load reduction rate", + }, + "ofpl_delay_time": { + "address": 0x00DD, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1000, + "signed": False, + "data_length": 1, + "data_unit": "second", + "description": "Overfrequency load reduction delay time", + }, + "ofpl_fstop_disch": { + "address": 0x00DE, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_length": 1, + "data_unit": "Hz", + "description": "OFPL fstop disch", + }, + "ofpl_fpmin": { + "address": 0x00DF, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_length": 1, + "data_unit": "Hz", + "description": "OFPL_fPmin", + }, + "user_password": { + "address": 0x00E0, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "User password", + }, + "advanced_password": { + "address": 0x00E1, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Advanced password", + }, + "ufpl_point": { + "address": 0x00E2, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_length": 1, + "data_unit": "Hz", + "description": "Underfrequency load increase point", + }, + "ufpl_set_rate": { + "address": 0x00E3, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "%", + "description": "Underfrequency load increase rate", + }, + "ufpl_delay_time": { + "address": 0x00E4, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1000, + "signed": False, + "data_length": 1, + "data_unit": "second", + "description": "Underfrequency load increase delay time", + }, + "ofpl_curve_type": { + "address": 0x00E5, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Overfrequency load reduction curve type selection.", + }, + "ofpl_tstop": { + "address": 0x00E6, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "second", + "description": "Overfrequency load reduction asymmetry curve stop time", + }, + "ofpl_remove_point": { + "address": 0x00E7, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_length": 1, + "data_unit": "Hz", + "description": "Overfrequency load reduction frequency remove point", + }, + "ufpl_remove_point": { + "address": 0x00E8, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_length": 1, + "data_unit": "Hz", + "description": "Underfrequency load increase frequency remove point", + }, + "export_soft_limit_enable": { + "address": 0x00E9, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Export Soft Limit En", + }, + "export_hard_limit_enable": { + "address": 0x00EA, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Export Hard Limit En", + }, + "general_soft_limit_enable": { + "address": 0x00EB, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "General Soft Limit En", + }, + "general_hard_limit_enable": { + "address": 0x00EC, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "General Hard Limit En", + }, + "w_ac_power_limit": { + "address": 0x00ED, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_length": 1, + "data_unit": "VA", + "description": "w Ac Power Limit", + }, + "connect_slop": { + "address": 0x00EE, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "%", + "description": "Connect slop", + }, + "reconnect_slop": { + "address": 0x00EF, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "%", + "description": "Reconnect slop", + }, + "hard_export_power": { + "address": 0x00F0, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_length": 1, + "data_unit": "W", + "description": "Hard Export Power", + }, + "hard_ac_power_t_limit": { + "address": 0x00F1, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_length": 1, + "data_unit": "VA", + "description": "Hard Ac Power t Limit", + }, + "setpoint_timeout": { + "address": 0x00F2, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1000, + "signed": False, + "data_length": 1, + "data_unit": "second", + "description": "Setpoint Timeout", + }, + "w_power_limit_gra": { + "address": 0x00F3, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10000, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "w Power Limit Gra", + }, + "pu_func_volt_response_v2": { + "address": 0x00F4, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_length": 1, + "data_unit": "V", + "description": "PuFunction Voltage", + }, + "pu_func_volt_response_v3": { + "address": 0x00F5, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_length": 1, + "data_unit": "V", + "description": "PuFunction Voltage", + }, + "pu_func_volt_response_v4": { + "address": 0x00F6, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_length": 1, + "data_unit": "V", + "description": "Pu Function Voltage", + }, + "pu_func_volt_response_v1": { + "address": 0x00F7, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_length": 1, + "data_unit": "V", + "description": "Pu Function Voltage", + }, + "pu_func_3tau": { + "address": 0x00F8, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Pu Func 3Tau", + }, + "pu_func_enable": { + "address": 0x00F9, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "PU Func Enable", + }, + "set_pu_power1": { + "address": 0x00FA, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "%", + "description": "Set Pu Power1", + }, + "set_pu_power2": { + "address": 0x00FB, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "%", + "description": "Set Pu Power2", + }, + "set_pu_power3": { + "address": 0x00FC, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "%", + "description": "Set Pu Power3", + }, + "set_pu_power4": { + "address": 0x00FD, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "%", + "description": "Set Pu Power4", + }, + "pu_tpye": { + "address": 0x00FF, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Pu Tpye", + }, + "ufpl_fstop_ch": { + "address": 0x0100, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_length": 1, + "data_unit": "Hz", + "description": "UFPL fstop ch", + }, + "ufpl_fpmax": { + "address": 0x0101, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 100, + "signed": False, + "data_length": 1, + "data_unit": "Hz", + "description": "UFPL fPmax", + }, + "drm_function_enable": { + "address": 0x0102, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "DRM Function Enable", + }, + "ct_type": { + "address": 0x0103, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Ct Type", + }, + "w_shadow_fix_func_enable": { + "address": 0x0104, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "w Shadow Fix Func Enable", + }, + "machine_type": { + "address": 0x0105, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Machine type: 1:X1, 3:X3", + }, + "phase_power_balance": { + "address": 0x0106, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Phase Power Balance", + }, + "w_machine_style": { + "address": 0x0107, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "w Machine Style", + }, + "meter_function": { + "address": 0x0108, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Meter Function", + }, + "meter1_id": { + "address": 0x0109, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Meter1 ID", + }, + "meter2_id": { + "address": 0x010A, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Meter2 ID", + }, + "direction_meter_ct_1": { + "address": 0x010B, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Direction Meter CT 1", + }, + "direction_meter_2": { + "address": 0x010C, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Direction Meter 2", + }, + "external_inv": { + "address": 0x010D, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "External Inv", + }, + "input_di_1": { + "address": 0x0110, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Input DI 1; 0:Low-level or 1:High-level", + }, + "disch_cut_off_point_different_enable": { + "address": 0x0111, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Whether Lead-acid Battery discharge cut-off voltage point is enable.", + }, + "disch_cut_off_voltage_grid_mode": { + "address": 0x0113, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_length": 1, + "data_unit": "V", + "description": "Lead-acid Battery discharge cut-off voltage in on-grid mode", + }, + "shadow_fix_func_enable_2": { + "address": 0x0114, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Shadow Fix Func Enable 2", + }, + "meter_ct_select": { + "address": 0x0115, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Meter/CT select", + }, + "fvrt_function": { + "address": 0x0116, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "FVRT Function", + }, + "fvrt_vac_upper": { + "address": 0x0117, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_length": 1, + "data_unit": "V", + "description": "If FVRT_Function is enable, FVRT Vac upper limit is available.", + }, + "fvrt_vac_lower": { + "address": 0x0118, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_length": 1, + "data_unit": "V", + "description": "If FVRT_Function is enable, FVRT Vac lower limit is available.", + }, + "bpv_connection_mode": { + "address": 0x011B, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "PV connection", + }, + "shut_down": { + "address": 0x011C, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Shut Down", + }, + "micro_grid": { + "address": 0x011D, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Micro Grid", + }, + "self_use_mode_backup_enable": { + "address": 0x011E, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Selfuse Mode Backup En", + }, + "b_self_use_backup_soc": { + "address": 0x011F, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "%", + "description": "b Self Use Backup Soc", + }, + "b_lease_mode_enable": { + "address": 0x0120, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "b Lease Mode Enable", + }, + "b_device_lock_flag": { + "address": 0x0121, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "b Device Lock Flag", + }, + "manual_mode_control": { + "address": 0x0122, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Manual Mode Control", + }, + "feedin_on_power": { + "address": 0x0123, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "W", + "description": "Grid connected pull in power point", + }, + "b_switch_on_soc": { + "address": 0x0124, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "%", + "description": "SOC trigger point of pull in action", + }, + "consume_off_power": { + "address": 0x0125, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "W", + "description": "Power consumption off trigger point", + }, + "b_switch_off_soc": { + "address": 0x0126, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "%", + "description": "SOC trigger point of breaking action", + }, + "minimum_per_on_signal": { + "address": 0x0127, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "min", + "description": "Minimum duration of single pull in", + }, + "maximum_per_day_on": { + "address": 0x0128, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Maximum cumulative pickup time of the day", + }, + "b_schedule_enable": { + "address": 0x0129, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "b Schedule Enable", + }, + "work_mode": { + "address": 0x012E, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Work Mode", + }, + "dry_contact_mode": { + "address": 0x012F, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Dry Contact Mode", + }, + "parallel_setting": { + "address": 0x0130, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "Parallel Setting", + }, + "external_gen_enable": { + "address": 0x0131, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "External Gen En", + }, + "external_gen_max_charge": { + "address": 0x0132, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 10, + "signed": False, + "data_length": 1, + "data_unit": "W", + "description": "External Gen Max Charge", + }, + "485_comm_fun_select": { + "address": 0x013E, + "register_type": "holding", + "data_format": "uint16", + "si_adj": 1, + "signed": False, + "data_length": 1, + "data_unit": "N/A", + "description": "485 Comm Fun Select", + }, + } + + def get_register_info(self, name: str) -> dict: + """Get the information about a register.""" + + return self._registers[name] + + def list_register_names(self) -> list: + """Return all registers defined in this class.""" + + return list(self._registers.keys()) + + def list_holding_registers(self) -> list: + """Return all holding registers defined in this class.""" + + f = lambda register: self._registers[register]["register_type"] == "holding" + return list(filter(f, self._registers)) + + def list_input_registers(self) -> list: + """Return all input registers defined in this class.""" + + f = lambda register: self._registers[register]["register_type"] == "input" + return list(filter(f, self._registers)) diff --git a/source/src/solaxx3/solaxx3.py b/source/src/solaxx3/solaxx3.py new file mode 100644 index 0000000..15ec4ce --- /dev/null +++ b/source/src/solaxx3/solaxx3.py @@ -0,0 +1,198 @@ +"""Class loading and storing data from the inverter.""" + +from datetime import datetime +from struct import unpack +from typing import Dict, Iterable, List, Literal, Tuple, Union + +from pymodbus.client import ModbusSerialClient + +from .solax_registers_info import FIELD_VALUES, FIELDS, SolaxRegistersInfo +from .utils import join_msb_lsb, twos_complement + +REGISTER_VALUE = Union[float, str, datetime] +REGISTER_INFO = Dict[FIELDS, FIELD_VALUES] + + +class SolaxX3: + """ + Class interacting with values from the inverter. + Initialization parameters: + - method (default: rtu) + - port (default: /dev/ttyUSB0 ) + - baudrate: Bits per second (default: 115,200 ) + - timeout: Timeout for a request, in seconds. (default: 3) + - parity: 'E'ven, 'O'dd, 'N'one (default: N) + - stopbits: Number of stop bits 0-2 (default: 1) + - bytesize: Number of bits per byte (7 or 8) (default: 8) + """ + + connected: bool = False + READ_BLOCK_LENGTH = 100 + + def __init__( + self, + method: str = "rtu", + port: str = "/dev/ttyUSB0", + baudrate: int = 115200, + timeout: int = 3, + parity: Literal["E", "O", "N"] = "N", + stopbits: Literal[0, 1, 2] = 1, + bytesize: Literal[7, 8] = 8, + ) -> None: + self._input_registers_values: List[int] = [] + self._holding_registers_values: List[int] = [] + + self.client: ModbusSerialClient = ModbusSerialClient( + method=method, + port=port, + baudrate=baudrate, + timeout=timeout, + parity=parity, + stopbits=stopbits, + bytesize=bytesize, + ) + + def connect(self) -> bool: + """Connect to the inverter and return if it was successful.""" + + self.connected: bool = self.client.connect() + return self.connected + + def _get_unsigned_16(self, value_type: str, address: int) -> int: + if value_type == "input": + return self._input_registers_values[address] + return self._holding_registers_values[address] + + def _read_register_range( + self, value_type: str, address: int, count: int = 1 + ) -> list: + if value_type == "input": + return self._input_registers_values[address : address + count] + return self._holding_registers_values[address : address + count] + + def _read_format_register_value( + self, register_info: REGISTER_INFO + ) -> REGISTER_VALUE: + """Read the values from a register based on length and sign + + Parameters: + register_info:dict - dictionary with register definition fields + """ + + if self._is_register_type_integer(register_info): + value = self._get_integer_value(register_info) + value = value / register_info["si_adj"] + + elif self._is_register_type_string(register_info): + value = self._get_string_value(register_info) + + else: + value = self._get_datetime_value(register_info) + + return value + + def _get_datetime_value(self, register_info: REGISTER_INFO) -> datetime: + register_type = register_info["register_type"] + + sec, minute, hr, day, mon, year = self._read_register_range( + register_type, register_info["address"], register_info["data_length"] + ) + inverter_datetime = f"{year:02}-{mon:02}-{day:02} {hr:02}:{minute:02}:{sec:02}" + value = datetime.strptime(inverter_datetime, "%y-%m-%d %H:%M:%S") + return value + + def _is_register_type_datetime(self, register_info: REGISTER_INFO) -> bool: + return "datetime" in register_info["data_format"] + + def _get_string_value(self, register_info: REGISTER_INFO) -> str: + characters: List[str] = [] + register_type: Literal["input", "holding"] = register_info["register_type"] + block = self._read_register_range( + register_type, register_info["address"], register_info["data_length"] + ) + + for i in range(register_info["data_length"]): + first_byte, second_byte = unpack("BB", int.to_bytes(block[i], 2, "little")) + if not second_byte == 0x0: + characters.append(chr(second_byte)) + if not first_byte == 0x0: + characters.append(chr(first_byte)) + + return "".join(characters) + + def _is_register_type_string(self, register_info: REGISTER_INFO) -> bool: + return "varchar" in register_info["data_format"] + + def _get_integer_value(self, register_info: REGISTER_INFO) -> int: + register_type = register_info["register_type"] + val = 0 + + if register_info["data_length"] == 1: + val = self._get_unsigned_16(register_type, register_info["address"]) + + if register_info["data_length"] == 2: + val = join_msb_lsb( + self._get_unsigned_16(register_type, register_info["address"] + 1), + self._get_unsigned_16(register_type, register_info["address"]), + ) + + if register_info["signed"]: + val = twos_complement(val, register_info["data_length"] * 16) + return val + + def _is_register_type_integer(self, register_info: REGISTER_INFO) -> bool: + return "int" in register_info["data_format"] + + def _read_register_value( + self, register_info: REGISTER_INFO + ) -> Tuple[REGISTER_VALUE, str]: + """Read the values from a register based on length and sign. + + Parameters: + :param register_info: dictionary with register definition fields + :return: Tuple containing the value and data unit + """ + + val = self._read_format_register_value(register_info) + return (val, register_info["data_unit"]) + + def read(self, name: str) -> Tuple[REGISTER_VALUE, str]: + """Retrieve the value for the register with the provided name""" + + registers = SolaxRegistersInfo() + register_info = registers.get_register_info(name) + value_data_unit = self._read_register_value(register_info) + return value_data_unit + + def list_register_names(self) -> list: + """Return all registers defined in register info.""" + + r = SolaxRegistersInfo() + return r.list_register_names() + + def read_all_registers(self) -> None: + """Read all register values from inverter.""" + + self._input_registers_values: List[int] = [] + self._holding_registers_values: List[int] = [] + + self._read_input_registers() + self._read_holding_registers() + + def _read_holding_registers(self): + for count in range(4): + address: int = count * self.READ_BLOCK_LENGTH + + values: Iterable = self.client.read_holding_registers( + address=address, count=self.READ_BLOCK_LENGTH, slave=1 + ).registers + self._holding_registers_values.extend(values) + + def _read_input_registers(self): + for count in range(4): + address: int = count * self.READ_BLOCK_LENGTH + + values: Iterable = self.client.read_input_registers( + address=address, count=self.READ_BLOCK_LENGTH, slave=1 + ).registers + self._input_registers_values.extend(values) diff --git a/source/src/solaxx3/utils.py b/source/src/solaxx3/utils.py new file mode 100644 index 0000000..288521a --- /dev/null +++ b/source/src/solaxx3/utils.py @@ -0,0 +1,18 @@ +"""SolaxX3 utils module performing binary operations.""" + + +def join_msb_lsb(msb: int, lsb: int) -> int: + """Join two 16-bit registers into a 32-bit register.""" + + return (msb << 16) | lsb + + +def twos_complement(number: int, bits: int) -> int: + """Compute the 2's complement of the provided integer.""" + + # if sign bit is set e.g., 8bit: 128-255 + if (number & (1 << (bits - 1))) != 0: + # compute negative value + number = number - (1 << bits) + + return number diff --git a/source/tests/final_result.py b/source/tests/final_result.py new file mode 100644 index 0000000..b2325e3 --- /dev/null +++ b/source/tests/final_result.py @@ -0,0 +1,361 @@ +"""The formatted register values.""" + +import datetime + + +altered_input_register_values = [ + 236.4, + 4.0, + 1243.0, + 629.5, + 737.1, + 1.4, + 0.6, + 49.96, + 44.0, + 2.0, + 940.0, + 484.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 428.1, + 0.2, + 128.0, + 1.0, + 29.0, + 1.0, + 0.0, + 2.0, + 98.0, + 3914.1, + 0.4, + 4535.7, + 8.2, + 5.2, + 30.0, + 12288.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 20792.6, + 3776.9, + 0.0, + 0.0, + 0.0, + 0.0, + 56.9, + 33161.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 235.9, + 4.0, + 937.0, + 49.97, + 232.3, + 1.3, + 45.0, + 49.98, + 235.4, + 1.5, + 261.0, + 49.98, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2.0, + 2.0, + 13476.8, + 107.5, + 69.1, + 0.0, + 0.0, + 0.2, + 35229.0, + 68.0, + 41.3, + 1.75, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 21.3, + 19.0, + 3.344, + 3.329, + 0.0, + 94.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -1247.0, + -1.0, + 2338.0, + -9973.0, + 5000.0, + -5000.0, + 0.0, + 0.0, + 245.0, + 12042.0, + 0.0, + 0.0, + 100.0, + 40.0, + 4.0, + 0.0, + 1.0, +] +altered_holding_register_values = [ + "H34T15H9022043", + "Solax ", + " ", + 900.0, + 15.0, + 900.0, + 195.5, + 264.5, + 49.5, + 50.5, + 1.0, + 0.0, + 259.9, + 195.5, + 264.5, + 47.5, + 51.5, + 100.0, + 0.0, + 1.0, + 1.0, + 1.0, + 0.94, + 0.9, + 20.0, + 50.0, + 80.0, + 100.0, + 1.05, + 1.0, + 6.0, + 0.0, + 0.0, + 213.9, + 223.1, + 236.9, + 246.1, + 0.0, + 10.0, + 0.0, + 0.0, + 20.0, + 5.0, + 0.0, + 7500.0, + -7500.0, + 47.55, + 50.05, + 195.5, + 253.0, + 0.0, + 0.0, + 47.55, + 50.05, + 195.5, + 253.0, + 0.0, + 1.0, + 900.0, + 29.0, + 0.0, + 1.0, + 1.0, + 6.0, + 27.0, + 8.0, + datetime.datetime(2024, 3, 19, 14, 58, 43), + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 20.0, + 21.0, + 0.0, + 30.0, + 0.0, + 30.0, + 0.0, + 0.0, + 0.0, + 245.0, + 0.0, + "SPNXLAQHWX", + 1.0, + 0.0, + 0.0, + 0.0, + 60000.0, + 1500.0, + 0.0, + 20.0, + 0.0, + 15000.0, + 0.0, + 0.0, + 0.3, + 0.1, + 0.1, + 0.1, + 0.3, + 0.01, + 0.01, + 0.01, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 32.0, + 1.0, + 50.2, + 5.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2014.0, + 49.8, + 2.0, + 0.005, + 0.0, + 30.0, + 50.2, + 49.8, + 0.0, + 0.0, + 0.0, + 0.0, + 3000.0, + 900.0, + 900.0, + 3000.0, + 3000.0, + 1.0, + 0.09, + 220.0, + 253.0, + 257.6, + 207.0, + 0.1, + 0.0, + 100.0, + 100.0, + 100.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 3.0, + 1.0, + 0.0, + 1.0, + 1.0, + 2.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 266.8, + 181.8, + 0.0, + 0.0, + 0.0, + 0.0, + 30.0, + 0.0, + 0.0, + 0.0, + 2000.0, + 80.0, + 1000.0, + 40.0, + 30.0, + 800.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 50.0, + 0.0, +] diff --git a/source/tests/mock_packages/mysql/__init__.py b/source/tests/mock_packages/mysql/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/source/tests/mock_packages/mysql/connection.py b/source/tests/mock_packages/mysql/connection.py new file mode 100644 index 0000000..f5d67da --- /dev/null +++ b/source/tests/mock_packages/mysql/connection.py @@ -0,0 +1,51 @@ +"""Module containing a connection class.""" + +from pathlib import Path + +from .cursor import Cursor +from .error import Error + +open_connections_file = Path(__file__).parent / "open_connections.txt" + + +class Connection: + """Class mocking a connection to a MySQL server.""" + + def __init__(self, *args, consider_open=True, **kwargs): + self.open_connections += consider_open + self.open: bool = consider_open + + def cursor(self) -> Cursor: + """Return the cursor for the connection.""" + + self._cursor = Cursor() + return self._cursor + + def close(self): + """Close connection.""" + + if not self.open: + raise Error("Connection already closed") + + self.open = False + self.open_connections -= 1 + + def commit(self): + """Save all changes to the database.""" + + def is_connected(self) -> bool: + """Return if the connection is open.""" + + return self.open + + @property + def open_connections(self): + with open(open_connections_file, "r", encoding="utf-8") as f: + contents = f.read() + return int(contents) + + @open_connections.setter + def open_connections(self, value: int): + value = value if value > 0 else 0 + with open(open_connections_file, "w", encoding="utf-8") as f: + f.write(str(value)) diff --git a/source/tests/mock_packages/mysql/connector.py b/source/tests/mock_packages/mysql/connector.py new file mode 100644 index 0000000..e2c9c4e --- /dev/null +++ b/source/tests/mock_packages/mysql/connector.py @@ -0,0 +1,10 @@ +"""Module with mock implementation of interacting with a MySQL server.""" + +from .connection import Connection +from .error import Error + + +def connect(*args, **kwargs) -> Connection: + """Mock connect to a MySQL server.""" + + return Connection() diff --git a/source/tests/mock_packages/mysql/cursor.py b/source/tests/mock_packages/mysql/cursor.py new file mode 100644 index 0000000..f9f9205 --- /dev/null +++ b/source/tests/mock_packages/mysql/cursor.py @@ -0,0 +1,56 @@ +"""Module with cursor implementation.""" + +import json +from pathlib import Path +from .error import Error + + +config_file = Path(__file__).parent / "cursor_config.json" + + +class Cursor: + """Cursor of a MySQL connection.""" + + def __init__(self, raise_err: bool = True): + if self._raise_error_in_init and raise_err: + raise Error + + def execute(self, *args, **kwargs) -> None: + """Mock execute a query.""" + + if self._raise_error: + raise Error + + def executemany(self, *args, **kwargs) -> None: + """Mock execute a query multiple times with different data.""" + + if self._raise_error: + raise Error + + @property + def _raise_error(self): + return self._read_configs()["raise_error"] + + @property + def _raise_error_in_init(self): + return self._read_configs()["raise_error_in_init"] + + @_raise_error.setter + def _raise_error(self, value: int): + data = self._read_configs() + data["raise_error"] = value + self._save_configs(data) + + @_raise_error_in_init.setter + def _raise_error_in_init(self, value: int): + data = self._read_configs() + data["raise_error_in_init"] = value + self._save_configs(data) + + def _read_configs(self): + with open(config_file, "r", encoding="utf-8") as f: + return json.loads(f.read()) + + def _save_configs(self, data): + with open(config_file, "w", encoding="utf-8") as f: + f.write(json.dumps(data, indent=4)) diff --git a/source/tests/mock_packages/mysql/cursor_config.json b/source/tests/mock_packages/mysql/cursor_config.json new file mode 100644 index 0000000..9f6c80c --- /dev/null +++ b/source/tests/mock_packages/mysql/cursor_config.json @@ -0,0 +1,4 @@ +{ + "raise_error_in_init": false, + "raise_error": false +} \ No newline at end of file diff --git a/source/tests/mock_packages/mysql/error.py b/source/tests/mock_packages/mysql/error.py new file mode 100644 index 0000000..9e28c0f --- /dev/null +++ b/source/tests/mock_packages/mysql/error.py @@ -0,0 +1,5 @@ +"""Module containing the basic error class of this package.""" + + +class Error(Exception): + """Basic error class of this package.""" diff --git a/source/tests/mock_packages/mysql/open_connections.txt b/source/tests/mock_packages/mysql/open_connections.txt new file mode 100644 index 0000000..c227083 --- /dev/null +++ b/source/tests/mock_packages/mysql/open_connections.txt @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/source/tests/mock_packages/pymodbus/__init__.py b/source/tests/mock_packages/pymodbus/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/source/tests/mock_packages/pymodbus/client.py b/source/tests/mock_packages/pymodbus/client.py new file mode 100644 index 0000000..1c1f25a --- /dev/null +++ b/source/tests/mock_packages/pymodbus/client.py @@ -0,0 +1,30 @@ +"""Class to mimick `pymodbus.client`. It is used when running the tests.""" + +from collections import namedtuple + +from .registers_output import raw_holding_register_values, raw_input_register_values +from .utils import getnext + +Registers = namedtuple("Registers", ["registers"]) + + +class ModbusSerialClient: + """Class mimicking `pymodbus.client.ModbusSerialClient`.""" + + def __init__(self, *_, **__) -> None: + self._holding_registers_gen = getnext(raw_holding_register_values) + self._input_registers_gen = getnext(raw_input_register_values) + + def connect(self): + """Mimick connecting the inverter and return the success code.""" + + return True + + def read_holding_registers(self, count, *_, **__): + """Read holding register values from inverter.""" + + return Registers([next(self._holding_registers_gen) for _ in range(count)]) + + def read_input_registers(self, count, *_, **__): + """Read input register values from inverter.""" + return Registers([next(self._input_registers_gen) for _ in range(count)]) diff --git a/source/tests/mock_packages/pymodbus/registers_output.py b/source/tests/mock_packages/pymodbus/registers_output.py new file mode 100644 index 0000000..3e5e100 --- /dev/null +++ b/source/tests/mock_packages/pymodbus/registers_output.py @@ -0,0 +1,806 @@ +"""Raw register values, just read from the inverter.""" + +raw_input_register_values = [ + 2364, + 40, + 1243, + 6295, + 7371, + 14, + 6, + 4996, + 44, + 2, + 940, + 484, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 4281, + 2, + 128, + 1, + 29, + 1, + 0, + 2, + 98, + 39141, + 0, + 0, + 4, + 45357, + 0, + 82, + 52, + 300, + 12288, + 0, + 1, + 0, + 52, + 10, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 47644, + 31, + 50010, + 5, + 0, + 0, + 0, + 0, + 569, + 0, + 3930, + 5, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2359, + 40, + 937, + 4997, + 2323, + 13, + 45, + 4998, + 2354, + 15, + 261, + 4998, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 2, + 0, + 3696, + 2, + 1075, + 0, + 0, + 0, + 691, + 0, + 0, + 0, + 2, + 0, + 24610, + 5, + 680, + 0, + 4130, + 0, + 175, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 213, + 190, + 3344, + 3329, + 0, + 94, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 64289, + 65535, + 65535, + 65535, + 2338, + 0, + 55563, + 65535, + 5000, + 0, + 60536, + 65535, + 0, + 0, + 0, + 0, + 245, + 0, + 12042, + 0, + 0, + 0, + 100, + 40, + 4, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, +] +raw_holding_register_values = [ + 18483, + 13396, + 12597, + 18489, + 12338, + 12848, + 13363, + 21359, + 27745, + 30752, + 8224, + 8224, + 8224, + 8224, + 8224, + 8224, + 8224, + 8224, + 8224, + 8224, + 8224, + 1800, + 900, + 15, + 900, + 1955, + 2645, + 4950, + 5050, + 1, + 0, + 2599, + 1955, + 2645, + 4750, + 5150, + 275, + 100, + 0, + 100, + 100, + 100, + 94, + 90, + 20, + 50, + 80, + 100, + 105, + 100, + 6, + 0, + 0, + 2139, + 2231, + 2369, + 2461, + 0, + 10, + 0, + 0, + 20, + 5, + 0, + 7500, + 58036, + 4755, + 5005, + 1955, + 2530, + 0, + 0, + 4755, + 5005, + 1955, + 2530, + 0, + 1, + 900, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 29, + 0, + 1, + 1, + 0, + 6, + 27, + 8, + 43, + 58, + 14, + 19, + 3, + 24, + 0, + 0, + 1, + 0, + 0, + 200, + 210, + 0, + 10240, + 30, + 25610, + 12830, + 0, + 0, + 0, + 15127, + 0, + 0, + 0, + 0, + 0, + 30, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2450, + 0, + 21328, + 20056, + 19521, + 20808, + 22360, + 1, + 0, + 0, + 0, + 0, + 0, + 60000, + 1500, + 0, + 20, + 0, + 15000, + 0, + 0, + 300, + 100, + 100, + 100, + 300, + 10, + 10, + 10, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 32, + 0, + 0, + 1, + 5020, + 5, + 0, + 0, + 0, + 0, + 2014, + 4980, + 2, + 5, + 0, + 30, + 5020, + 4980, + 0, + 0, + 0, + 0, + 30000, + 900, + 900, + 30000, + 30000, + 1000, + 900, + 2200, + 2530, + 2576, + 2070, + 10, + 0, + 100, + 100, + 100, + 0, + 10, + 1, + 0, + 0, + 0, + 0, + 0, + 3, + 1, + 0, + 1, + 1, + 2, + 0, + 0, + 0, + 100, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 2668, + 1818, + 0, + 0, + 0, + 0, + 0, + 0, + 30, + 0, + 0, + 0, + 2000, + 80, + 1000, + 40, + 30, + 800, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 500, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 20, + 95, + 1000, + 30, + 60, + 0, + 15127, + 0, + 2000, + 0, + 0, + 2000, + 30, + 80, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, +] diff --git a/source/tests/mock_packages/pymodbus/utils.py b/source/tests/mock_packages/pymodbus/utils.py new file mode 100644 index 0000000..47c24fe --- /dev/null +++ b/source/tests/mock_packages/pymodbus/utils.py @@ -0,0 +1,11 @@ +"""Mock `pymodbus` utils.""" + +from typing import Any + + +def getnext(iterable: list, default: Any = 0): + """Yield one item at a time and yield `default` when iterable is exhausted.""" + + yield from iterable + while True: + yield default diff --git a/source/tests/test_mysql_connector.py b/source/tests/test_mysql_connector.py new file mode 100644 index 0000000..27258e3 --- /dev/null +++ b/source/tests/test_mysql_connector.py @@ -0,0 +1,68 @@ +import sys +from os import environ +from pathlib import Path +from unittest import TestCase + +from tests.mock_packages.mysql.connection import Connection +from tests.mock_packages.mysql.cursor import Cursor + +environ["MYSQL_DB_USERNAME"] = "" +environ["MYSQL_DB_HOST_IP"] = "" +environ["MYSQL_DB_PASSWORD"] = "" +environ["MYSQL_DB_DATABASE"] = "" +current: Path = Path().resolve() +paths_to_extend = [current / "tests" / "mock_packages", current / "src"] +for path in paths_to_extend: + path_as_string = str(path) + if path_as_string not in sys.path: + sys.path.append(path_as_string) + +from database import read_and_save + + +class MysqlConnectionLeakageTests(TestCase): + """Tests to see if the class leaves any connections open after its processes.""" + + def _open_connections(self): + return Connection(consider_open=False).open_connections + + def setUp(self) -> None: + """Set up the data after each test case.""" + + Cursor(raise_err=False)._raise_error = False + Cursor(raise_err=False)._raise_error_in_init = False + Connection().open_connections = 0 + + def test_bulk_transfer_without_error(self): + """Test if `bulk_transfer` leaves any hanging connections in normal operations.""" + + for _ in range(3): + read_and_save.bulk_save() + + self.assertEqual(self._open_connections(), 0) + + def test_bulk_transfer_with_error_in_cursor_init(self): + """Test if `bulk_transfer` leaves any hanging connections with errors when instantiating + cursor.""" + + Cursor()._raise_error_in_init = True + for _ in range(3): + try: + read_and_save.bulk_save() + except: + pass + + self.assertEqual(self._open_connections(), 0) + + def test_bulk_transfer_with_error_in_cursor(self): + """Test if `bulk_transfer` leaves any hanging connections with errors when executing + a query in the cursor.""" + + Cursor()._raise_error = True + for _ in range(3): + try: + read_and_save.bulk_save() + except: + pass + + self.assertEqual(self._open_connections(), 0) diff --git a/source/tests/test_register_data.py b/source/tests/test_register_data.py new file mode 100644 index 0000000..18f7bbd --- /dev/null +++ b/source/tests/test_register_data.py @@ -0,0 +1,254 @@ +""" +Test scenarios: + Report any of the following anomalies: + 1. Not valid dictionary syntax [done] + 2. Dictionary key duplicates [done] + 3. "address" duplicates + 4. The value field has different keys than: [done] + * address + * register_type + * data_format + * si_adj + * signed + * data_unit + * data_length + * description + 5. "dataformat" starts with 'uint' but "signed" is different than "False" + 6. "dataformat" starts with 'int' but "signed" is different than "True" + 7. If "data_format" contains "int" in the name, + "data_length" != / 16 or 1 if no number is found + in "data_length" + 8. "data_unit" is not one of following: + {%, A, C, Hx, KWh, N/A, V, VA, Var, W, Wh, hour, second} + Note: values are case sensitive + 9. "register_type" is different than {input, holding} + Note: no values are case sensitive + 10. the key name contains "volt" but the "data_unit" is different than "V" + 11. the key name contains "current" but the "data_unit" is different than "A" + 12. "address" overlapping +""" + +import re +import sys +import unittest +from itertools import filterfalse +from pathlib import Path + +from src.solaxx3.solax_registers_info import SolaxRegistersInfo + +import_path = Path().resolve() / "tests" / "mock_packages" +if str(import_path) not in sys.path: + sys.path.append(str(import_path)) + +REGISTER_FILE = "src/solaxx3/solax_registers_info.py" + + +class RegisterTests(unittest.TestCase): + """Tests for SolaxRegistersInfo.""" + + registers = SolaxRegistersInfo._registers + + def test_tc1_valid_dictionary(self): + """Prints an error if `registers` is a dictionary.""" + + self.assertIsInstance(self.registers, dict) + + def test_tc2_duplicate_keys(self): + """Prints an error if 'registers' has any duplicate keys.""" + + PATTERN = r""" + "(?P.*?)" # The key of the dictionary + \s*: # colon + \s*{ # start of value + """ + regex = re.compile(PATTERN, re.VERBOSE) + + with open(REGISTER_FILE, "r", encoding="utf-8") as dictionary: + list_keys = [] + + for line_number, line in filterfalse( + lambda x: x[1].lstrip().startswith("#"), enumerate(dictionary, start=1) + ): + re_match = regex.search(line) + + if re_match is not None: + key = re_match.group("key") + self.assertNotIn(key, list_keys, f"line={line_number}") + list_keys.append(key) + + def test_tc4_correct_value_field(self): + """Prints an error if the keys do not have the following keys: + - address, register_type, data_format, si_adj, signed, data_unit, data_length, + description""" + + VALID_SUBKEYS = [ + "address", + "data_format", + "data_length", + "data_unit", + "description", + "register_type", + "si_adj", + "signed", + ] + + for register, register_value in self.registers.items(): + self.assertListEqual( + sorted(list(register_value.keys())), VALID_SUBKEYS, f"dict={register!r}" + ) + + def test_tc5_correctly_unsigned(self): + """Prints an error if subkey `data_format` starts with `uint` but `signed` is + different than False.""" + + for register, register_value in self.registers.items(): + self.assertFalse( + register_value["data_format"].startswith("uint") + and register_value["signed"] is not False, + f"not correctly signed: dict='{register}'", + ) + + def test_tc6_correctly_signed(self): + """Prints an error if subkey `data_format` starts with 'int' but `signed` + is different than True.""" + + for register, register_value in self.registers.items(): + self.assertFalse( + register_value["data_format"].startswith("int") + and register_value["signed"] is not True, + f"not correctly signed: dict='{register}'", + ) + + def test_tc7_correct_data_length(self): + """Prints an error if 'data_format' does not match 'data_length'.""" + + PATTERN = r"int(?P\d+)" + regex = re.compile(PATTERN) + + for register, register_value in self.registers.items(): + match = regex.search(register_value["data_format"]) + + if match: + bits = int(match.group("bits")) + self.assertEqual( + bits / 16, register_value["data_length"], f"dict={register!r}" + ) + + def test_tc8_correct_data_unit_value(self): + """Prints an error if `data_unit` is not %, C, Hz, KWh, N/A, V, VA, Var, + W, Wh, hour, second.""" + + POSSIBLE_DATA_UNITS = ( + "min", + "bps", + "%", + "A", + "C", + "Hz", + "KWh", + "N/A", + "V", + "VA", + "Var", + "W", + "Wh", + "hour", + "second", + ) + + for register, register_value in self.registers.items(): + self.assertGreater( + len(register_value["data_unit"]), + 0, + f"empty 'data_unit': dict={register!r}", + ) + self.assertIn( + register_value["data_unit"], + POSSIBLE_DATA_UNITS, + f"invalid data_unit: '{register_value['data_unit']}'; 'dict='{register}'", + ) + + def test_tc9_correct_register_type(self): + """Prints an error if `register_type` is not 'input' or 'holding'.""" + + POSSIBLE_REGISTER_TYPES = ("input", "holding") + + for register, register_value in self.registers.items(): + self.assertIn( + register_value["register_type"], + POSSIBLE_REGISTER_TYPES, + f"dict='{register}'", + ) + + def test_tc10_correct_data_unit_with_volt(self): + """Prints an error if the key contains 'volt' but `data_unit` is differrent + than 'V'.""" + + for register, register_value in self.registers.items(): + self.assertFalse( + "volt" in register + and register_value["data_unit"] != "V" + and "percent" not in register + and "ratio" not in register, + f"'data_unit' does not match name: dict={register!r}", + ) + + def test_tc11_correct_data_unit_with_current(self): + """Prints an error if the key contains 'current' but `data_unit` is + differrent than 'A'.""" + + for register, register_value in self.registers.items(): + self.assertFalse( + "current" in register and register_value["data_unit"] != "A", + f"'data_unit' does not match name: dict='{register}'", + ) + + def test_tc12_correct_addresses(self): + """Prints an error if there is any address overlapping.""" + + holding_addresses: list = [] + input_addresses: list = [] + + for register, register_value in self.registers.items(): + addresses = ( + holding_addresses + if register_value["register_type"] == "holding" + else input_addresses + ) + data_length = register_value["data_length"] + + for x in range(data_length): + address = register_value["address"] + x + self.assertNotIn(address, addresses, f"dict={register!r}") + addresses.append(address) + + def test_tc13_subkey_duplicates(self): + """Prints an error if there are any subkey duplicates.""" + + PATTERN = r""" + "(?P.*?)" # dictionary subkey + \s*: # colon + \s*?[^[]+$ # value different than a dict + """ + + regex = re.compile(PATTERN, re.VERBOSE) + + with open(REGISTER_FILE, "r", encoding="utf-8") as dictionary: + subkeys = [] + + for line_number, line in filterfalse( + lambda x: x[1].lstrip().startswith("#"), enumerate(dictionary, start=1) + ): + match = regex.search(line) + + if match: + subkey = match.group("subkey") + + self.assertNotIn(subkey, subkeys, f"line={line_number}") + subkeys.append(subkey) + else: + subkeys.clear() + + +if __name__ == "__main__": + unittest.main() diff --git a/source/tests/test_solaxx3.py b/source/tests/test_solaxx3.py new file mode 100644 index 0000000..ab476d1 --- /dev/null +++ b/source/tests/test_solaxx3.py @@ -0,0 +1,48 @@ +"""Module to test solaxx3.""" + +import sys +import unittest +from pathlib import Path + +from src.solaxx3 import solaxx3 +from tests.final_result import ( + altered_holding_register_values, + altered_input_register_values, +) + +import_path = Path().resolve() / "tests" / "mock_packages" +if str(import_path) not in sys.path: + sys.path.append(str(import_path)) + + +class Solaxx3Tests(unittest.TestCase): + """Tests for SolaxX3.""" + + def test_connection(self): + """Test if SolaxX3 connected.""" + + s = solaxx3.SolaxX3() + s.connect() + self.assertEqual(s.connected, True) + + def test_format_registers(self): + """Test if the module correctly formats raw register values.""" + + result = [] + s = solaxx3.SolaxX3() + s.read_all_registers() + + regs = solaxx3.SolaxRegistersInfo().list_register_names() + for reg in regs: + result.append(s.read(reg)[0]) + + self.assertEqual( + result, altered_input_register_values + altered_holding_register_values + ) + + def test_are_registers_identical(self): + """Test if holding registers' values are not equal to input registers' values.""" + + s = solaxx3.SolaxX3() + s.read_all_registers() + self.assertFalse(s._holding_registers_values == s._input_registers_values)