From 4783bc91a5dfb2ff795acca986d58e5139228cd0 Mon Sep 17 00:00:00 2001 From: Dos Moonen Date: Fri, 26 Jan 2024 09:54:51 +0100 Subject: [PATCH 1/2] Add support for X1 Mini G3 (FW Version 3) (#139) * Add support for X1 Mini G3 (FW Version 3) --------- Co-authored-by: broglep <20624281+broglep@users.noreply.github.com> Co-authored-by: Dos Moonen --- .github/workflows/tests.yaml | 2 +- solax/__init__.py | 1 + solax/inverter.py | 2 +- solax/inverters/x1_mini_v34.py | 1 + solax/units.py | 1 + solax/utils.py | 6 +- tests/fixtures.py | 12 ++++ tests/samples/expected_values.py | 20 ++++++ tests/samples/responses.py | 109 +++++++++++++++++++++++++++++++ verify.sh | 2 +- 10 files changed, 149 insertions(+), 7 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 4bc4929..f35651d 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -32,7 +32,7 @@ jobs: isort --check-only --profile black . - name: Lint with flake8 run: | - flake8 --ignore=E501 solax tests + flake8 --ignore=E501,E704 solax tests - name: Lint with pylint run: | pylint -d 'C0111' solax tests diff --git a/solax/__init__.py b/solax/__init__.py index 5953b06..6fd9817 100644 --- a/solax/__init__.py +++ b/solax/__init__.py @@ -1,4 +1,5 @@ """Support for Solax inverter via local API.""" + import asyncio import logging diff --git a/solax/inverter.py b/solax/inverter.py index df3042c..5304263 100644 --- a/solax/inverter.py +++ b/solax/inverter.py @@ -106,4 +106,4 @@ def schema(cls) -> vol.Schema: return cls._schema def __str__(self) -> str: - return f"{self.__class__.__name__} :: {self.http_client}" + return f"{self.__class__.__name__}::{self.http_client}" diff --git a/solax/inverters/x1_mini_v34.py b/solax/inverters/x1_mini_v34.py index 5d3b4cf..90906ed 100644 --- a/solax/inverters/x1_mini_v34.py +++ b/solax/inverters/x1_mini_v34.py @@ -27,6 +27,7 @@ class X1MiniV34(Inverter): [vol.Coerce(float)], vol.Any( vol.Length(min=69, max=69), + vol.Length(min=100, max=100), vol.Length(min=200, max=200), ), ) diff --git a/solax/units.py b/solax/units.py index 2ac09b8..f8ca1c3 100644 --- a/solax/units.py +++ b/solax/units.py @@ -1,4 +1,5 @@ """ Units and different measurement types""" + from enum import Enum from typing import NamedTuple, Union diff --git a/solax/utils.py b/solax/utils.py index fcabfb1..f2a884f 100644 --- a/solax/utils.py +++ b/solax/utils.py @@ -10,8 +10,7 @@ class Packer(Protocol): # pragma: no cover data into one raw value """ - def __call__(self, *vals: float) -> float: - ... + def __call__(self, *vals: float) -> float: ... PackerBuilderResult = Tuple[Tuple[int, ...], Packer] @@ -24,8 +23,7 @@ class PackerBuilder(Protocol): # pragma: no cover raw values to be fed to the packer """ - def __call__(self, *indexes: int) -> PackerBuilderResult: - ... + def __call__(self, *indexes: int) -> PackerBuilderResult: ... def __u16_packer(*values: float) -> float: diff --git a/tests/fixtures.py b/tests/fixtures.py index 4868ff7..6df29c1 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -9,6 +9,7 @@ X1_HYBRID_G4_VALUES, X1_MINI_VALUES, X1_MINI_VALUES_V34, + X1_MINI_VALUES_V34_VER3, X1_SMART_VALUES, X1_VALUES, X3_HYBRID_G4_VALUES, @@ -28,6 +29,7 @@ X1_HYBRID_G3_RESPONSE, X1_HYBRID_G4_RESPONSE, X1_MINI_RESPONSE_V34, + X1_MINI_RESPONSE_V34_VER3, X1_SMART_RESPONSE, X3_HYBRID_G3_2X_MPPT_RESPONSE, X3_HYBRID_G3_2X_MPPT_RESPONSE_V34, @@ -238,6 +240,16 @@ def simple_http_fixture(httpserver): headers=None, data=None, ), + InverterUnderTest( + uri="/", + method="POST", + query_string=None, + response=X1_MINI_RESPONSE_V34_VER3, + inverter=inverter.X1MiniV34, + values=X1_MINI_VALUES_V34_VER3, + headers=None, + data="optType=ReadRealTimeData", + ), ] diff --git a/tests/samples/expected_values.py b/tests/samples/expected_values.py index f3d7a05..849bff2 100644 --- a/tests/samples/expected_values.py +++ b/tests/samples/expected_values.py @@ -365,6 +365,26 @@ "Inverter Temperature": 41, } + +X1_MINI_VALUES_V34_VER3 = { + "Network Voltage": 234.8, + "Output Current": 0.7, + "AC Power": 144.0, + "PV1 Voltage": 96.6, + "PV2 Voltage": 0.0, + "PV1 Current": 1.6, + "PV2 Current": 0.0, + "PV1 Power": 154.0, + "PV2 Power": 0.0, + "Grid Frequency": 49.94, + "Total Energy": 6.7, + "Today's Energy": 3.1, + "Total Feed-in Energy": 2.5, + "Total Consumption": 0.0, + "Power Now": 0.0, + "Inverter Temperature": 39.0, +} + X1_SMART_VALUES = { "Network Voltage": 239.6, "Output Current": 12.6, diff --git a/tests/samples/responses.py b/tests/samples/responses.py index ad65b18..04f9a93 100644 --- a/tests/samples/responses.py +++ b/tests/samples/responses.py @@ -441,6 +441,115 @@ "Information": [0.700, 4, "XXXXXXXXXXXXXX", 1, 1.19, 0.00, 1.32, 0.00, 0.00, 1], } +X1_MINI_RESPONSE_V34_VER3 = { + "sn": "XXXXXXXXXX", + "ver": "3.006.04", + "type": 4, + "Data": [ + 2348, + 7, + 144, + 966, + 0, + 16, + 0, + 154, + 0, + 4994, + 2, + 67, + 0, + 31, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 34, + 0, + 25, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 39, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 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": [1.500, 4, "XXXXXXXXXXXXXX", 8, 2.27, 0.00, 1.43, 0.00, 0.00, 1], +} + X1_SMART_RESPONSE = { "sn": "XXXXXXX", "ver": "2.033.20", diff --git a/verify.sh b/verify.sh index 74e34f1..7ea6cca 100755 --- a/verify.sh +++ b/verify.sh @@ -17,7 +17,7 @@ echo "Running mypy..." mypy --exclude venv . echo "Running flake8..." -flake8 --ignore=E501 solax tests +flake8 --ignore=E501,E704 solax tests echo "Running pylint..." pylint -d 'C0111' solax tests From ce0732f689f68ece8e7c86ed08e49ff2e287f272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szil=C3=A1rd=20K=C3=A1losi?= Date: Mon, 12 Feb 2024 00:06:41 +0100 Subject: [PATCH 2/2] fix(x1_boost): totals are consecutive u16 ints (#129) * fix(x1_boost): totals are consecutive u16 ints * modify test cases from real world example * old test cases * formating * utils format * missing fixture --- solax/inverters/x1_boost.py | 8 +- tests/fixtures.py | 12 ++ tests/samples/expected_values.py | 19 +++ tests/samples/responses.py | 209 +++++++++++++++++++++++++++++++ 4 files changed, 244 insertions(+), 4 deletions(-) diff --git a/solax/inverters/x1_boost.py b/solax/inverters/x1_boost.py index 0ca8f44..58e92ab 100644 --- a/solax/inverters/x1_boost.py +++ b/solax/inverters/x1_boost.py @@ -3,7 +3,7 @@ from solax import utils from solax.inverter import Inverter, InverterHttpClient, Method, ResponseParser from solax.units import Total, Units -from solax.utils import div10, div100, to_signed +from solax.utils import div10, div100, pack_u16, to_signed class X1Boost(Inverter): @@ -46,12 +46,12 @@ def response_decoder(cls): "PV1 Power": (7, Units.W), "PV2 Power": (8, Units.W), "AC Frequency": (9, Units.HZ, div100), - "Total Generated Energy": (11, Total(Units.KWH), div10), + "Total Generated Energy": (pack_u16(11, 12), Total(Units.KWH), div10), "Today's Generated Energy": (13, Total(Units.KWH), div10), "Inverter Temperature": (39, Units.C), "Exported Power": (48, Units.W, to_signed), - "Total Export Energy": (50, Total(Units.KWH), div100), - "Total Import Energy": (52, Total(Units.KWH), div100), + "Total Export Energy": (pack_u16(50, 51), Total(Units.KWH), div100), + "Total Import Energy": (pack_u16(52, 53), Total(Units.KWH), div100), } @classmethod diff --git a/tests/fixtures.py b/tests/fixtures.py index 6df29c1..3983bfb 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -6,6 +6,7 @@ from tests.samples.expected_values import ( QVOLTHYBG33P_VALUES, X1_BOOST_VALUES, + X1_BOOST_VALUES_OVERFLOWN, X1_HYBRID_G4_VALUES, X1_MINI_VALUES, X1_MINI_VALUES_V34, @@ -25,6 +26,7 @@ QVOLTHYBG33P_RESPONSE_V34, X1_BOOST_AIR_MINI_RESPONSE, X1_BOOST_RESPONSE, + X1_BOOST_RESPONSE_OVERFLOWN, X1_HYBRID_G3_2X_MPPT_RESPONSE, X1_HYBRID_G3_RESPONSE, X1_HYBRID_G4_RESPONSE, @@ -130,6 +132,16 @@ def simple_http_fixture(httpserver): headers=X_FORWARDED_HEADER, data=None, ), + InverterUnderTest( + uri="/", + method="POST", + query_string="optType=ReadRealTimeData", + response=X1_BOOST_RESPONSE_OVERFLOWN, + inverter=inverter.X1Boost, + values=X1_BOOST_VALUES_OVERFLOWN, + headers=X_FORWARDED_HEADER, + data=None, + ), InverterUnderTest( uri="/", method="POST", diff --git a/tests/samples/expected_values.py b/tests/samples/expected_values.py index 849bff2..905b215 100644 --- a/tests/samples/expected_values.py +++ b/tests/samples/expected_values.py @@ -423,6 +423,25 @@ "Total Import Energy": 81.84, } +X1_BOOST_VALUES_OVERFLOWN = { + "AC Voltage": 237.4, + "AC Output Current": 6.7, + "AC Output Power": 1581, + "PV1 Voltage": 397.3, + "PV2 Voltage": 233.8, + "PV1 Current": 4.1, + "PV2 Current": 5.7, + "PV1 Power": 1628, + "PV2 Power": 1336, + "AC Frequency": 49.96, + "Total Generated Energy": 14055.6, + "Today's Generated Energy": 1.3, + "Inverter Temperature": 36, + "Exported Power": 1444, + "Total Export Energy": 11348.18, + "Total Import Energy": 1850.05, +} + QVOLTHYBG33P_VALUES = { "Network Voltage Phase 1": 221.4, "Network Voltage Phase 2": 223.8, diff --git a/tests/samples/responses.py b/tests/samples/responses.py index 04f9a93..64304a9 100644 --- a/tests/samples/responses.py +++ b/tests/samples/responses.py @@ -968,6 +968,215 @@ "Information": [5.0, 4, "XXXXXXX", 1, 2.21, 0.00, 1.38, 0.00, 0.00, 1], } +X1_BOOST_RESPONSE_OVERFLOWN = { + "sn": "XXXXXXX", + "ver": "2.034.06", + "type": 4, + "Data": [ + 2374, + 67, + 1581, + 3973, + 2338, + 41, + 57, + 1628, + 1336, + 4996, + 2, + 9484, + 2, + 13, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 36, + 0, + 10636, + 0, + 0, + 0, + 0, + 0, + 0, + 1444, + 0, + 20706, + 17, + 53933, + 2, + 0, + 39, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 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": [3.680, 4, "XXXXXXX", 1, 2.06, 0, 1.27, 0, 0, 1], +} + X3_MIC_RESPONSE = { "type": "X3-MIC", "SN": "XXXXXXX",