Skip to content

Commit

Permalink
Added support for X3 Hybrid G4 (#100)
Browse files Browse the repository at this point in the history
* Added support for X3 Hybrid G4

* Fixed regression in to_signed

* Fixed whitespace

* Limited connection variants for X3HybridG4

* Fixed formating

* Fixed tests
  • Loading branch information
adambogocz authored Dec 21, 2022
1 parent 5c5097e commit 5fdb6d1
Show file tree
Hide file tree
Showing 9 changed files with 488 additions and 3 deletions.
2 changes: 2 additions & 0 deletions solax/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
X1Mini,
X1MiniV34,
X1Smart,
X3HybridG4,
XHybrid,
)

Expand All @@ -17,6 +18,7 @@
XHybrid,
X3,
X3V34,
X3HybridG4,
X1,
X1Mini,
X1MiniV34,
Expand Down
2 changes: 2 additions & 0 deletions solax/inverters/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .x1_mini_v34 import X1MiniV34
from .x1_smart import X1Smart
from .x3 import X3
from .x3_hybrid_g4 import X3HybridG4
from .x3_v34 import X3V34
from .x_hybrid import XHybrid

Expand All @@ -17,6 +18,7 @@
"X1MiniV34",
"X1Smart",
"X3V34",
"X3HybridG4",
"X3",
"X1Boost",
"X1HybridGen4",
Expand Down
93 changes: 93 additions & 0 deletions solax/inverters/x3_hybrid_g4.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
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


class X3HybridG4(Inverter):
"""X3 Hybrid G4 v3.006.04"""

# pylint: disable=duplicate-code
_schema = vol.Schema(
{
vol.Required("type"): vol.All(int, 14),
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),
"PV1 Current": (12, Units.A, div10),
"PV2 Current": (13, Units.A, div10),
"PV1 Power": (14, Units.W),
"PV2 Power": (15, 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),
"Run mode text": (19, Units.NONE, X3HybridG4._decode_run_mode),
"EPS 1 Voltage": (23, Units.W, div10),
"EPS 2 Voltage": (24, Units.W, div10),
"EPS 3 Voltage": (25, Units.W, div10),
"EPS 1 Current": (26, Units.W, twoway_div10),
"EPS 2 Current": (27, Units.W, twoway_div10),
"EPS 3 Current": (28, Units.W, 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),
"Feed-in Power ": (pack_u16(34, 35), Units.W, to_signed32),
"Battery Power": (41, Units.W, to_signed),
"Yield total": (pack_u16(68, 69), Total(Units.KWH), div10),
"Yield today": (70, Units.KWH, div10),
"Feed-in Energy": (pack_u16(86, 87), Total(Units.KWH), div100),
"Consumed Energy": (pack_u16(88, 89), Total(Units.KWH), div100),
"Battery Remaining Capacity": (103, Units.PERCENT),
"Battery Temperature": (105, Units.C, to_signed),
"Battery Voltage": (pack_u16(169, 170), Units.V, div100),
}

# pylint: enable=duplicate-code
6 changes: 3 additions & 3 deletions solax/units.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
""" Units and different measrement types"""
""" Units and different measurement types"""
from enum import Enum
from typing import NamedTuple, Union

Expand All @@ -17,14 +17,14 @@ class Units(Enum):


class Measurement(NamedTuple):
"""Respresention of measurement with a given unit and arbitrary values."""
"""Representation of measurement with a given unit and arbitrary values."""

unit: Units
is_monotonic: bool = False


class Total(Measurement):
"""A Measuremeant where the values are continuously increasing."""
"""A Measurement where the values are continuously increasing."""

is_monotonic: bool = True

Expand Down
7 changes: 7 additions & 0 deletions solax/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ def div100(val):


INT16_MAX = 0x7FFF
INT32_MAX = 0x7FFFFFFF


def to_signed(val):
Expand All @@ -73,6 +74,12 @@ def to_signed(val):
return val


def to_signed32(val):
if val > INT32_MAX:
val -= 2**32
return val


def twoway_div10(val):
return to_signed(val) / 10

Expand Down
12 changes: 12 additions & 0 deletions tests/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
X1_MINI_VALUES_V34,
X1_SMART_VALUES,
X1_VALUES,
X3_HYBRID_G4_VALUES,
X3_HYBRID_VALUES,
X3_VALUES,
X3V34_HYBRID_VALUES,
Expand All @@ -32,6 +33,7 @@
X3_HYBRID_G3_2X_MPPT_RESPONSE_V34_EPS_MODE,
X3_HYBRID_G3_2X_MPPT_RESPONSE_V34_NEGATIVE_POWER,
X3_HYBRID_G3_RESPONSE,
X3_HYBRID_G4_RESPONSE,
X3_MIC_RESPONSE,
XHYBRID_DE01_RESPONSE,
XHYBRID_DE02_RESPONSE,
Expand Down Expand Up @@ -204,6 +206,16 @@ def simple_http_fixture(httpserver):
headers=None,
data=None,
),
InverterUnderTest(
uri="/",
method="POST",
query_string=None,
response=X3_HYBRID_G4_RESPONSE,
inverter=inverter.X3HybridG4,
values=X3_HYBRID_G4_VALUES,
headers=None,
data="optType=ReadRealTimeData",
),
InverterUnderTest(
uri="/",
method="POST",
Expand Down
41 changes: 41 additions & 0 deletions tests/samples/expected_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,47 @@
"EPS Total Energy": 1.7,
}

X3_HYBRID_G4_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,
"Feed-in Power ": -152.0,
"Battery Power": 0.0,
"Yield total": 4.4,
"Yield today": 0.1,
"Feed-in Energy": 0.0,
"Consumed Energy": 20.09,
"Battery Remaining Capacity": 30.0,
"Battery Temperature": 22.0,
"Battery Voltage": 234.6,
}

X1_VALUES = {
"PV1 Current": 0,
"PV2 Current": 1,
Expand Down
Loading

0 comments on commit 5fdb6d1

Please sign in to comment.