diff --git a/solax/inverters/__init__.py b/solax/inverters/__init__.py index 883e9d3..039c947 100644 --- a/solax/inverters/__init__.py +++ b/solax/inverters/__init__.py @@ -8,6 +8,7 @@ from .x3 import X3 from .x3_hybrid_g4 import X3HybridG4 from .x3_mic_pro_g2 import X3MicProG2 +from .x3_ultra import X3Ultra from .x3_v34 import X3V34 from .x_hybrid import XHybrid @@ -24,4 +25,5 @@ "X1Boost", "X1HybridGen4", "X3MicProG2", + "X3Ultra", ] diff --git a/solax/inverters/x3_ultra.py b/solax/inverters/x3_ultra.py new file mode 100644 index 0000000..42c0f08 --- /dev/null +++ b/solax/inverters/x3_ultra.py @@ -0,0 +1,128 @@ +import voluptuous as vol + +from solax.inverter import Inverter +from solax.units import Total, Units +from solax.utils import ( + div10, + div100, + pack_u16, + to_signed, + to_signed32, + twoway_div10, + twoway_div100, +) + + +class X3Ultra(Inverter): + """X3 Ultra v1.001.20""" + + # pylint: disable=duplicate-code + _schema = vol.Schema( + { + vol.Required("type"): vol.All(int, 25), + vol.Required("sn"): str, + vol.Required("ver"): str, + vol.Required("Data"): vol.Schema( + vol.All( + [vol.Coerce(float)], + vol.Length(min=300, max=300), + ) + ), + vol.Required("Information"): vol.Schema( + vol.All(vol.Length(min=10, max=10)) + ), + }, + extra=vol.REMOVE_EXTRA, + ) + + @classmethod + def build_all_variants(cls, host, port, pwd=""): + return [cls._build(host, port, pwd, False)] + + @classmethod + def _decode_run_mode(cls, run_mode): + return { + 0: "Waiting", + 1: "Checking", + 2: "Normal", + 3: "Fault", + 4: "Permanent Fault", + 5: "Updating", + 6: "EPS Check", + 7: "EPS Mode", + 8: "Self Test", + 9: "Idle", + 10: "Standby", + }.get(run_mode) + + @classmethod + def response_decoder(cls): + return { + "Grid 1 Voltage": (0, Units.V, div10), + "Grid 2 Voltage": (1, Units.V, div10), + "Grid 3 Voltage": (2, Units.V, div10), + "Grid 1 Current": (3, Units.A, twoway_div10), + "Grid 2 Current": (4, Units.A, twoway_div10), + "Grid 3 Current": (5, Units.A, twoway_div10), + "Grid 1 Power": (6, Units.W, to_signed), + "Grid 2 Power": (7, Units.W, to_signed), + "Grid 3 Power": (8, Units.W, to_signed), + "PV1 Voltage": (10, Units.V, div10), + "PV2 Voltage": (11, Units.V, div10), + "PV3 Voltage": (129, Units.V, div10), + "PV1 Current": (12, Units.A, div10), + "PV2 Current": (13, Units.A, div10), + "PV3 Current": (130, Units.A, div10), + "PV1 Power": (14, Units.W), + "PV2 Power": (15, Units.W), + "PV3 Power": (131, Units.W), + "Grid 1 Frequency": (16, Units.HZ, div100), + "Grid 2 Frequency": (17, Units.HZ, div100), + "Grid 3 Frequency": (18, Units.HZ, div100), + # "Run mode": (19, Units.NONE), # Only use the index once due to HA uids + "Run mode text": (19, Units.NONE, X3Ultra._decode_run_mode), + "EPS 1 Voltage": (23, Units.V, div10), + "EPS 2 Voltage": (24, Units.V, div10), + "EPS 3 Voltage": (25, Units.V, div10), + "EPS 1 Current": (26, Units.A, twoway_div10), + "EPS 2 Current": (27, Units.A, twoway_div10), + "EPS 3 Current": (28, Units.A, twoway_div10), + "EPS 1 Power": (29, Units.W, to_signed), + "EPS 2 Power": (30, Units.W, to_signed), + "EPS 3 Power": (31, Units.W, to_signed), + "Grid Power ": (pack_u16(34, 35), Units.W, to_signed32), + "Battery 1 Voltage": (39, Units.V, div10), + "Battery 2 Voltage": (132, Units.V, div10), + "Battery 1 Current": (40, Units.A, twoway_div100), + "Battery 2 Current": (133, Units.A, twoway_div100), + "Battery 1 Power": (41, Units.W, to_signed), + "Battery 2 Power": (134, Units.W, to_signed), + "Battery 1 Remaining Capacity": (103, Units.PERCENT), + "Battery 2 Remaining Capacity": (140, Units.PERCENT), + "Battery 1 Temperature": (105, Units.C, to_signed), + "Battery 2 Temperature": (142, Units.C, to_signed), + "Load/Generator Power": (47, Units.W, to_signed), + "Radiator Temperature": (54, Units.C, to_signed), + "Yield total": (pack_u16(58, 59), Total(Units.KWH), div10), + "Yield today": (70, Units.KWH, div10), + "Battery Discharge Energy total": ( + pack_u16(74, 75), + Total(Units.KWH), + div10, + ), + "Battery Charge Energy total": (pack_u16(76, 77), Total(Units.KWH), div10), + "Battery Discharge Energy today": (78, Units.KWH, div10), + "Battery Charge Energy today": (79, Units.KWH, div10), + "PV Energy total": (pack_u16(80, 81), Total(Units.KWH), div10), + "EPS Energy total": (pack_u16(83, 84), Total(Units.KWH), div10), + "EPS Energy today": (85, Units.KWH, div10), + "Feed-in Energy total": (pack_u16(86, 87), Total(Units.KWH), div100), + "Grid Consumed Energy total": (pack_u16(88, 89), Total(Units.KWH), div100), + "Feed-in Energy today": (pack_u16(90, 91), Total(Units.KWH), div100), + "Grid Consumed Energy today": (pack_u16(92, 93), Total(Units.KWH), div100), + "Battery Remaining Capacity": (158, Units.PERCENT), + "Battery Remaining Energy": (106, Units.KWH, div10), + "Inverter Power": (159, Units.W, div10), + } + + # pylint: enable=duplicate-code diff --git a/tests/fixtures.py b/tests/fixtures.py index 3983bfb..7cf9d07 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -16,6 +16,7 @@ X3_HYBRID_G4_VALUES, X3_HYBRID_VALUES, X3_MICPRO_G2_VALUES, + X3_ULTRA_VALUES, X3_VALUES, X3V34_HYBRID_VALUES, X3V34_HYBRID_VALUES_EPS_MODE, @@ -41,6 +42,7 @@ X3_HYBRID_G4_RESPONSE, X3_MIC_RESPONSE, X3_MICPRO_G2_RESPONSE, + X3_ULTRA_RESPONSE, XHYBRID_DE01_RESPONSE, XHYBRID_DE02_RESPONSE, ) @@ -242,6 +244,16 @@ def simple_http_fixture(httpserver): headers=None, data="optType=ReadRealTimeData", ), + InverterUnderTest( + uri="/", + method="POST", + query_string=None, + response=X3_ULTRA_RESPONSE, + inverter=inverter.X3Ultra, + values=X3_ULTRA_VALUES, + headers=None, + data="optType=ReadRealTimeData", + ), InverterUnderTest( uri="/", method="POST", diff --git a/tests/samples/expected_values.py b/tests/samples/expected_values.py index 4af3422..19a211e 100644 --- a/tests/samples/expected_values.py +++ b/tests/samples/expected_values.py @@ -521,3 +521,59 @@ "Total feed-in energy": 286.7, "Total consumption": 6.2, } + +X3_ULTRA_VALUES = { + "Grid 1 Voltage": 238.3, + "Grid 2 Voltage": 239.7, + "Grid 3 Voltage": 237.7, + "Grid 1 Current": 1.0, + "Grid 2 Current": 1.1, + "Grid 3 Current": 1.1, + "Grid 1 Power": 33.0, + "Grid 2 Power": 81.0, + "Grid 3 Power": 76.0, + "PV1 Voltage": 248.3, + "PV2 Voltage": 230.5, + "PV1 Current": 0.1, + "PV2 Current": 0.2, + "PV1 Power": 45.0, + "PV2 Power": 58.0, + "Grid 1 Frequency": 50.01, + "Grid 2 Frequency": 50.01, + "Grid 3 Frequency": 50.01, + "Run mode": 2.0, + "Run mode text": "Normal", + "EPS 1 Voltage": 0.0, + "EPS 2 Voltage": 0.0, + "EPS 3 Voltage": 0.0, + "EPS 1 Current": 0.0, + "EPS 2 Current": 0.0, + "EPS 3 Current": 0.0, + "EPS 1 Power": 0.0, + "EPS 2 Power": 0.0, + "EPS 3 Power": 0.0, + "Grid Power ": -152.0, + "Battery Current": 0.0, + "Battery Power": 0.0, + "Load/Generator Power": 342.0, + "Radiator Temperature": 23.0, + "Yield total": 4.4, + "Yield today": 0.1, + "Battery Discharge Energy total": 2.4, + "Battery Charge Energy total": 1.0, + "Battery Discharge Energy today": 0.0, + "Battery Charge Energy today": 0.0, + "PV Energy total": 3.4, + "EPS Energy total": 0.0, + "EPS Energy today": 0.0, + "Feed-in Energy": 0.0, + "Consumed Energy": 20.09, + "Feed-in Energy total": 0.0, + "Consumed Energy total": 2.09, + "Battery Remaining Capacity": 30.0, + "Battery Temperature": 22.0, + "Battery Remaining Energy": 3.3, + "Battery mode": 0.0, + "Battery mode text": "Self Use Mode", + "Battery Voltage": 234.6, +} diff --git a/tests/samples/responses.py b/tests/samples/responses.py index 0e4078f..a5de230 100644 --- a/tests/samples/responses.py +++ b/tests/samples/responses.py @@ -3031,6 +3031,314 @@ "Information": [4.000, 16, "MC20XXXXXXXXXX", 8, 1.20, 0.00, 1.18, 1.00, 0.00, 1], } +X3_ULTRA_RESPONSE = { + "sn": "SN********", + "ver": "1.001.20", + "type": 25, + "Data": [ + 2328, + 2338, + 2320, + 20, + 19, + 19, + 216, + 249, + 199, + 0, + 0, + 2498, + 0, + 65, + 0, + 1629, + 5000, + 5000, + 5000, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 52, + 0, + 0, + 0, + 0, + 2138, + 960, + 2057, + 2159, + 78, + 0, + 1, + 27, + 65484, + 256, + 6159, + 7436, + 6147, + 100, + 0, + 34, + 0, + 0, + 0, + 1984, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 24, + 23, + 0, + 1, + 1092, + 0, + 1228, + 0, + 2, + 27, + 1229, + 0, + 26, + 0, + 0, + 0, + 2771, + 0, + 12812, + 0, + 10, + 0, + 860, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 28, + 1, + 13, + 17, + 256, + 2336, + 1600, + 120, + 234, + 138, + 125, + 34, + 34, + 9, + 3378, + 3366, + 60289, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2454, + 105, + 2592, + 2129, + 740, + 1595, + 2159, + 78, + 0, + 0, + 1, + 28, + 1, + 12, + 17, + 256, + 2336, + 1600, + 120, + 234, + 136, + 121, + 34, + 34, + 9, + 3379, + 3368, + 3652, + 0, + 28, + 664, + 0, + 62029, + 1, + 1684, + 0, + 0, + 0, + 1684, + 0, + 20564, + 12339, + 19009, + 12866, + 16693, + 12612, + 14646, + 20564, + 12339, + 19009, + 12866, + 16693, + 12612, + 13880, + 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, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "Information": [25.000, 25, "H3BC25********", 13, 4.06, 0.00, 4.04, 0.04, 0.00, 1], +} QVOLTHYBG33P_RESPONSE_V34 = { "sn": "SWXXXX", "ver": "2.034.06",