Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for new Ginlong/Solis Wifi stick (S3-WIFI-ST) #290

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions omnikinverter/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,40 @@ def from_tcp(data: dict[str, Any]) -> Inverter:
),
)

@staticmethod
def from_cgi(data: str) -> Inverter:
"""Return Inverter object from the Omnik Inverter response.

Args:
data: The CGI (webscraping) data from the Omnik Inverter.

Returns:
An Inverter object.
"""

split_data = data.split(";")
if len(split_data) < 7:
raise OmnikInverterWrongSourceError(
"Your inverter has no data source from cgi."
)

def try_parse_float(item):
try:
return float(item)
except:
return None

return Inverter(
serial_number=split_data[0],
model=split_data[2],
firmware=split_data[1],
firmware_slave=None,
solar_rated_power=None,
solar_current_power=try_parse_float(split_data[4]),
solar_energy_today=try_parse_float(split_data[5]),
solar_energy_total=try_parse_float(split_data[6]),
koyote marked this conversation as resolved.
Show resolved Hide resolved
)


@dataclass
class Device:
Expand Down Expand Up @@ -285,3 +319,32 @@ def get_value(search_key: str) -> Any:
firmware=get_value("version"),
ip_address=get_value("wanIp"),
)

@staticmethod
def from_cgi(data: str) -> Device:
"""Return Device object from the Omnik Inverter response.

Args:
data: The CGI (webscraping) data from the Omnik Inverter.

Returns:
A Device object.
"""

split_data = data.split(";")
if len(split_data) < 9:
raise OmnikInverterWrongSourceError(
"Your inverter has no device data from cgi."
)

def try_parse_int(item):
try:
return int(item)
except:
return None

return Device(
signal_quality=try_parse_int(split_data[8]),
firmware=split_data[1],
ip_address=split_data[9],
)
8 changes: 7 additions & 1 deletion omnikinverter/omnikinverter.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ async def request(

# Use big try to make sure manual session is always cleaned up
try:
if self.source_type == "html" and (
if (self.source_type == "html" or self.source_type == "cgi") and (
self.username is None or self.password is None
):
raise OmnikInverterAuthError(
Expand Down Expand Up @@ -179,6 +179,9 @@ async def inverter(self) -> Inverter:
if self.source_type == "html":
data = await self.request("status.html")
return Inverter.from_html(data)
if self.source_type == "cgi":
data = await self.request("inverter.cgi", params={"t": "123"})
koyote marked this conversation as resolved.
Show resolved Hide resolved
return Inverter.from_cgi(data)
if self.source_type == "javascript":
data = await self.request("js/status.js")
return Inverter.from_js(data)
Expand All @@ -203,6 +206,9 @@ async def device(self) -> Device:
if self.source_type == "html":
data = await self.request("status.html")
return Device.from_html(data)
if self.source_type == "cgi":
data = await self.request("moniter.cgi", params={"t": "123"})
return Device.from_cgi(data)
if self.source_type == "javascript":
data = await self.request("js/status.js")
return Device.from_js(data)
Expand Down
1 change: 1 addition & 0 deletions tests/fixtures/inverter.cgi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
12345678910;3280031;205;38.4;780;23.799999;d;NO;
koyote marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions tests/fixtures/moniter.cgi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
5A1227251CB01447;00010130;Disable;null;null;null;Enable;SSID NAME;60;192.168.0.106;BE:BA:DE:FF:1C:6B;Connected;Unconnected;
61 changes: 61 additions & 0 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,67 @@ async def test_device_html(aresponses: ResponsesMockServer) -> None:
assert device.firmware == "ME_08_0102_2.03"
assert device.ip_address == "192.168.0.106"

@pytest.mark.asyncio
async def test_inverter_cgi(aresponses: ResponsesMockServer) -> None:
"""Test request from an Inverter - CGI source."""
aresponses.add(
"example.com",
"/inverter.cgi",
"GET",
aresponses.Response(
status=200,
headers={"Content-Type": "text/html"},
text=load_fixtures("inverter.cgi"),
),
)

async with aiohttp.ClientSession() as session:
client = OmnikInverter(
host="example.com",
source_type="cgi",
username="klaas",
password="supercool",
session=session,
)
inverter: Inverter = await client.inverter()
assert inverter
assert inverter.serial_number == "12345678910"
assert inverter.firmware == "3280031"
assert inverter.firmware_slave is None
assert inverter.model == "205"
assert inverter.solar_rated_power is None
assert inverter.solar_current_power == 780
assert inverter.solar_energy_today == 23.799999
assert inverter.solar_energy_total is None


@pytest.mark.asyncio
async def test_device_cgi(aresponses: ResponsesMockServer) -> None:
"""Test request from a Device - CGI source."""
aresponses.add(
"example.com",
"/moniter.cgi",
"GET",
aresponses.Response(
status=200,
headers={"Content-Type": "text/html"},
text=load_fixtures("moniter.cgi"),
),
)

async with aiohttp.ClientSession() as session:
client = OmnikInverter(
host="example.com",
source_type="cgi",
username="klaas",
password="supercool",
session=session,
)
device: Device = await client.device()
assert device
assert device.signal_quality == 60
assert device.firmware == "00010130"
assert device.ip_address == "192.168.0.106"

@pytest.mark.asyncio
async def test_inverter_without_session(aresponses: ResponsesMockServer) -> None:
Expand Down