Skip to content

Commit 172575d

Browse files
authored
add get_pvis() and get_powermeters() (#82)
1 parent 2456621 commit 172575d

File tree

2 files changed

+128
-5
lines changed

2 files changed

+128
-5
lines changed

e3dc/_e3dc.py

Lines changed: 95 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
)
2323
from ._e3dc_rscp_web import E3DC_RSCP_web
2424
from ._rscpLib import rscpFindTag, rscpFindTagIndex
25-
from ._rscpTags import RscpTag, RscpType, getStrPowermeterType
25+
from ._rscpTags import RscpTag, RscpType, getStrPowermeterType, getStrPviType
2626

2727
REMOTE_ADDRESS = "https://s10.e3dc.com/s10/phpcmd/cmd.php"
2828
REQUEST_INTERVAL_SEC = 10 # minimum interval between requests
@@ -1136,6 +1136,47 @@ def get_system_status(self, keepAlive=False):
11361136
outObj = {k: SystemStatusBools[v] for k, v in outObj.items()}
11371137
return outObj
11381138

1139+
def get_batteries(self, keepAlive=False):
1140+
"""Scans for installed batteries via rscp protocol locally.
1141+
1142+
Args:
1143+
keepAlive (Optional[bool]): True to keep connection alive
1144+
Returns:
1145+
list[dict]: List containing the found batteries as follows.:
1146+
[
1147+
{'index': 0, "dcbs": 3}
1148+
]
1149+
"""
1150+
maxBatteries = 8
1151+
outObj = []
1152+
for batIndex in range(maxBatteries):
1153+
try:
1154+
req = self.sendRequest(
1155+
(
1156+
RscpTag.BAT_REQ_DATA,
1157+
RscpType.Container,
1158+
[
1159+
(RscpTag.BAT_INDEX, RscpType.Uint16, batIndex),
1160+
(RscpTag.BAT_REQ_DCB_COUNT, RscpType.NoneType, None),
1161+
],
1162+
),
1163+
keepAlive=True if batIndex < (maxBatteries - 1) else keepAlive,
1164+
)
1165+
except NotAvailableError:
1166+
continue
1167+
1168+
dcbCount = rscpFindTagIndex(req, RscpTag.BAT_DCB_COUNT)
1169+
1170+
if dcbCount is not None:
1171+
outObj.append(
1172+
{
1173+
"index": batIndex,
1174+
"dcbs": dcbCount,
1175+
}
1176+
)
1177+
1178+
return outObj
1179+
11391180
def get_battery_data(self, batIndex=None, dcbs=None, keepAlive=False):
11401181
"""Polls the battery data via rscp protocol locally.
11411182
@@ -1491,6 +1532,55 @@ def get_batteries_data(self, batteries=None, keepAlive=False):
14911532

14921533
return outObj
14931534

1535+
def get_pvis(self, keepAlive=False):
1536+
"""Scans for installed pvis via rscp protocol locally.
1537+
1538+
Args:
1539+
keepAlive (Optional[bool]): True to keep connection alive
1540+
Returns:
1541+
list[dict]: List containing the found pvis as follows.::
1542+
[
1543+
{'index': 0, "phases": 3, "strings": 2, 'type': 3, 'typeName': 'PVI_TYPE_E3DC_E'}
1544+
]
1545+
"""
1546+
maxPvis = 8
1547+
outObj = []
1548+
for pviIndex in range(maxPvis):
1549+
req = self.sendRequest(
1550+
(
1551+
RscpTag.PVI_REQ_DATA,
1552+
"Container",
1553+
[
1554+
(RscpTag.PVI_INDEX, RscpType.Uint16, pviIndex),
1555+
(RscpTag.PVI_REQ_TYPE, RscpType.NoneType, None),
1556+
(RscpTag.PVI_REQ_USED_STRING_COUNT, RscpType.NoneType, None),
1557+
(RscpTag.PVI_REQ_AC_MAX_PHASE_COUNT, RscpType.NoneType, None),
1558+
],
1559+
),
1560+
keepAlive=True if pviIndex < (maxPvis - 1) else keepAlive,
1561+
)
1562+
1563+
pviType = rscpFindTagIndex(req, RscpTag.PVI_TYPE)
1564+
1565+
if pviType is not None:
1566+
maxPhaseCount = int(
1567+
rscpFindTagIndex(req, RscpTag.PVI_AC_MAX_PHASE_COUNT)
1568+
)
1569+
usedStringCount = int(
1570+
rscpFindTagIndex(req, RscpTag.PVI_USED_STRING_COUNT)
1571+
)
1572+
outObj.append(
1573+
{
1574+
"index": pviIndex,
1575+
"phases": maxPhaseCount,
1576+
"strings": usedStringCount,
1577+
"type": pviType,
1578+
"typeName": getStrPviType(pviType),
1579+
}
1580+
)
1581+
1582+
return outObj
1583+
14941584
def get_pvi_data(self, pviIndex=None, strings=None, phases=None, keepAlive=False):
14951585
"""Polls the inverter data via rscp protocol locally.
14961586
@@ -1830,9 +1920,9 @@ def get_powermeters(self, keepAlive=False):
18301920
keepAlive (Optional[bool]): True to keep connection alive
18311921
18321922
Returns:
1833-
dict: Dictionary containing the found powermeters as follows.::
1923+
list[dict]: List containing the found powermeters as follows.::
18341924
1835-
"powermeters": [
1925+
[
18361926
{'index': 0, 'type': 1, 'typeName': 'PM_TYPE_ROOT'},
18371927
{'index': 1, 'type': 4, 'typeName': 'PM_TYPE_ADDITIONAL_CONSUMPTION'}
18381928
]
@@ -1842,7 +1932,7 @@ def get_powermeters(self, keepAlive=False):
18421932
for pmIndex in range(
18431933
maxPowermeters
18441934
): # max 8 powermeters according to E3DC spec
1845-
res = self.sendRequest(
1935+
req = self.sendRequest(
18461936
(
18471937
RscpTag.PM_REQ_DATA,
18481938
RscpType.Container,
@@ -1854,7 +1944,7 @@ def get_powermeters(self, keepAlive=False):
18541944
keepAlive=True if pmIndex < (maxPowermeters - 1) else keepAlive,
18551945
)
18561946

1857-
pmType = rscpFindTagIndex(res, RscpTag.PM_TYPE)
1947+
pmType = rscpFindTagIndex(req, RscpTag.PM_TYPE)
18581948

18591949
if pmType is not None:
18601950
outObj.append(

e3dc/_rscpTags.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3623,6 +3623,19 @@ class PowermeterType(Enum):
36233623
PM_TYPE_FARM_ADDITIONAL = 0x08
36243624

36253625

3626+
class PviType(Enum):
3627+
"""All available pvi types."""
3628+
3629+
PVI_TYPE_UNDEFINED = 0x00
3630+
PVI_TYPE_SOLU = 0x01
3631+
PVI_TYPE_KACO = 0x02
3632+
PVI_TYPE_E3DC_E = 0x03
3633+
PVI_TYPE_E3DC_MINI = 0x04
3634+
PVI_TYPE_UNDEFINED_6 = 0x06
3635+
PVI_TYPE_UNDEFINED_7 = 0x07
3636+
PVI_TYPE_UNDEFINED_8 = 0x08
3637+
3638+
36263639
def getRscpTag(tag: int | str | RscpTag) -> RscpTag:
36273640
"""Convert a tag to its RscpTag enumeration equivalent.
36283641
@@ -3792,3 +3805,23 @@ def getStrPowermeterType(powermetertype: int | str | PowermeterType) -> str:
37923805
powermetertype = PowermeterType[powermetertype]
37933806

37943807
return powermetertype.name
3808+
3809+
3810+
def getStrPviType(pvitype: int | str | PviType) -> str:
3811+
"""Convert a pvi type to its string name representation in PviType enumeration.
3812+
3813+
Args:
3814+
pvitype (int | str | PviType): The pvi type to be converted.
3815+
- If int, it's assumed to be the pvi type value.
3816+
- If str, it's assumed to be the pvi type name.
3817+
- If PviType, its name is used.
3818+
3819+
Returns:
3820+
str: The name of the pvi type as a string.
3821+
"""
3822+
if isinstance(pvitype, int):
3823+
pvitype = PviType(pvitype)
3824+
elif isinstance(pvitype, str):
3825+
pvitype = PviType[pvitype]
3826+
3827+
return pvitype.name

0 commit comments

Comments
 (0)