Skip to content

Commit

Permalink
Add a simpler entry point that bypasses the discovery process
Browse files Browse the repository at this point in the history
in the case where the Inverter type is already known.

Some inverters at the end of the REGISTRY (e.g. X1HybridGen4) randomly
crash because the discovery code issues too many incorrect requests
before sending the correct one.
This patch adds a variant of the entry point in which one can specify
the name of the inverter type.

This addresses home-assistant/core#66617
and home-assistant/core#99421
  • Loading branch information
brew-your-own committed Jan 21, 2024
1 parent c35be4a commit 8e42f91
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 1 deletion.
8 changes: 7 additions & 1 deletion solax/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from async_timeout import timeout

from solax.discovery import discover
from solax.discovery import discover, discover_single_inverter
from solax.inverter import Inverter, InverterResponse

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -36,6 +36,12 @@ async def real_time_api(ip_address, port=80, pwd=""):
return RealTimeAPI(i)


# bypass the full discovery process, and only do it for the specified inverter type
async def real_time_api_inverter(inverter_name, ip_address, port=80, pwd=""):
i = await discover_single_inverter(inverter_name, ip_address, port, pwd)
return RealTimeAPI(i)


class RealTimeAPI:
"""Solax inverter real time API"""

Expand Down
37 changes: 37 additions & 0 deletions solax/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,37 @@ async def discover(self, host, port, pwd="") -> Inverter:
)
raise DiscoveryError(msg)

async def discover_single_inverter(
self, inverter_name, host, port, pwd=""
) -> Inverter:
found = False
for invclass in REGISTRY:
if invclass.__name__ == inverter_name:
found = True
for i in invclass.build_all_variants(host, port, pwd):
task = asyncio.create_task(self._discovery_task(i), name=f"{i}")
task.add_done_callback(self._task_handler)
self._tasks.add(task)

if not found:
raise DiscoveryError(f"Unknown inverter type {inverter_name}")

while len(self._tasks) > 0:
logging.debug("%d discovery tasks are still running...", len(self._tasks))
await asyncio.sleep(0.5)

if self._discovered_inverter is not None:
logging.info("Discovered inverter: %s", self._discovered_inverter)
return self._discovered_inverter

msg = (
"Unable to connect to the inverter at "
f"host={host} port={port} of type {inverter_name}.\n"
"Please see https://github.com/squishykid/solax/wiki/DiscoveryError\n"
f"Failures={str(self._failures)}"
)
raise DiscoveryError(msg)


class DiscoveryError(Exception):
"""Raised when unable to discover inverter"""
Expand All @@ -101,3 +132,9 @@ async def discover(host, port, pwd="") -> Inverter:
discover_state = DiscoveryState()
await discover_state.discover(host, port, pwd)
return discover_state.get_discovered_inverter()


async def discover_single_inverter(inverter, host, port, pwd="") -> Inverter:
discover_state = DiscoveryState()
await discover_state.discover_single_inverter(inverter, host, port, pwd)
return discover_state.get_discovered_inverter()
19 changes: 19 additions & 0 deletions tests/test_discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,31 @@ async def test_discovery(inverters_fixture):
assert rt_api.inverter.__class__ == inverter_class


@pytest.mark.asyncio
async def test_discovery_single_inverter(inverters_fixture):
conn, inverter_class, _ = inverters_fixture
rt_api = await solax.real_time_api_inverter(inverter_class.__name__, *conn)
assert rt_api.inverter.__class__ == inverter_class


@pytest.mark.asyncio
async def test_discovery_unsupported_inverter():
with pytest.raises(DiscoveryError):
await solax.real_time_api_inverter("doesnotexist", "localhost", 2)


@pytest.mark.asyncio
async def test_discovery_no_host():
with pytest.raises(DiscoveryError):
await solax.real_time_api("localhost", 2)


@pytest.mark.asyncio
async def test_discovery_single_inverter_no_host():
with pytest.raises(DiscoveryError):
await solax.real_time_api_inverter("X1HybridGen4", "localhost", 2)


@pytest.mark.asyncio
async def test_discovery_no_host_with_pwd():
with pytest.raises(DiscoveryError):
Expand Down

0 comments on commit 8e42f91

Please sign in to comment.