Compare commits

...

3 Commits

Author SHA1 Message Date
Stefan Allius
cf94cb19f2 Merge branch 'renovate/ghcr.io-hassio-addons-base-18.x' of https://github.com/s-allius/tsun-gen3-proxy into renovate/ghcr.io-hassio-addons-base-18.x 2025-07-16 20:10:48 +02:00
Stefan Allius
7889e329ef Save a tast reference
Important: Save a reference of the created task,
to avoid a task disappearing mid-execution. The
event loop only keeps weak references to tasks.
A task that isn’t referenced elsewhere may get
garbage collected at any time, even before it’s
done. For reliable “fire-and-forget” background
tasks, gather them in a collection
2025-07-15 23:23:35 +02:00
Stefan Allius
8c3f3ba827 S allius/issue472 (#473)
* catch socket.gaierror exception

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-15 21:09:29 +02:00
5 changed files with 34 additions and 10 deletions

View File

@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [unreleased] ## [unreleased]
- save task references, to avoid a task disappearing mid-execution
- catch socket.gaierror exception and log this with info level
- Update dependency coverage to v7.9.2 - Update dependency coverage to v7.9.2
- add-on: bump base-image to version 18.0.3 - add-on: bump base-image to version 18.0.3
- add-on: remove armhf and armv7 support - add-on: remove armhf and armv7 support

View File

@@ -327,6 +327,7 @@ class SolarmanV5(SolarmanBase):
self.sensor_list = 0 self.sensor_list = 0
self.mb_regs = [{'addr': 0x3000, 'len': 48}, self.mb_regs = [{'addr': 0x3000, 'len': 48},
{'addr': 0x2000, 'len': 96}] {'addr': 0x2000, 'len': 96}]
self.background_tasks = set()
''' '''
Our puplic methods Our puplic methods
@@ -339,6 +340,7 @@ class SolarmanV5(SolarmanBase):
self.inverter = None self.inverter = None
self.switch.clear() self.switch.clear()
self.log_lvl.clear() self.log_lvl.clear()
self.background_tasks.clear()
super().close() super().close()
def send_start_cmd(self, snr: int, host: str, def send_start_cmd(self, snr: int, host: str,
@@ -690,8 +692,10 @@ class SolarmanV5(SolarmanBase):
self.__forward_msg() self.__forward_msg()
def publish_mqtt(self, key, data): # pragma: no cover def publish_mqtt(self, key, data): # pragma: no cover
asyncio.ensure_future( task = asyncio.ensure_future(
Proxy.mqtt.publish(key, data)) Proxy.mqtt.publish(key, data))
self.background_tasks.add(task)
task.add_done_callback(self.background_tasks.discard)
def get_cmd_rsp_log_lvl(self) -> int: def get_cmd_rsp_log_lvl(self) -> int:
ftype = self.ifc.rx_peek()[self.header_len] ftype = self.ifc.rx_peek()[self.header_len]

View File

@@ -4,6 +4,7 @@ import logging
import traceback import traceback
import json import json
import gc import gc
import socket
from aiomqtt import MqttCodeError from aiomqtt import MqttCodeError
from asyncio import StreamReader, StreamWriter from asyncio import StreamReader, StreamWriter
from ipaddress import ip_address from ipaddress import ip_address
@@ -38,6 +39,7 @@ class InverterBase(InverterIfc, Proxy):
self.use_emulation = False self.use_emulation = False
self.__ha_restarts = -1 self.__ha_restarts = -1
self.remote = StreamPtr(None) self.remote = StreamPtr(None)
self.background_tasks = set()
ifc = AsyncStreamServer(reader, writer, ifc = AsyncStreamServer(reader, writer,
self.async_publ_mqtt, self.async_publ_mqtt,
self.create_remote, self.create_remote,
@@ -72,6 +74,7 @@ class InverterBase(InverterIfc, Proxy):
if self.remote.ifc: if self.remote.ifc:
self.remote.ifc.close() self.remote.ifc.close()
self.remote.ifc = None self.remote.ifc = None
self.background_tasks.clear()
async def disc(self, shutdown_started=False) -> None: async def disc(self, shutdown_started=False) -> None:
if self.remote.stream: if self.remote.stream:
@@ -136,9 +139,14 @@ class InverterBase(InverterIfc, Proxy):
logging.info(f'[{self.remote.stream.node_id}:' logging.info(f'[{self.remote.stream.node_id}:'
f'{self.remote.stream.conn_no}] ' f'{self.remote.stream.conn_no}] '
f'Connected to {addr}') f'Connected to {addr}')
asyncio.create_task(self.remote.ifc.client_loop(addr)) task = asyncio.create_task(
self.remote.ifc.client_loop(addr))
self.background_tasks.add(task)
task.add_done_callback(self.background_tasks.discard)
except (ConnectionRefusedError, TimeoutError) as error: except (ConnectionRefusedError,
TimeoutError,
socket.gaierror) as error:
logging.info(f'{error}') logging.info(f'{error}')
except Exception: except Exception:
Infos.inc_counter('SW_Exception') Infos.inc_counter('SW_Exception')

View File

@@ -43,6 +43,7 @@ class ModbusTcp():
def __init__(self, loop, tim_restart=10) -> None: def __init__(self, loop, tim_restart=10) -> None:
self.tim_restart = tim_restart self.tim_restart = tim_restart
self.background_tasks = set()
inverters = Config.get('inverters') inverters = Config.get('inverters')
batteries = Config.get('batteries') batteries = Config.get('batteries')
@@ -54,10 +55,13 @@ class ModbusTcp():
and 'client_mode' in inv): and 'client_mode' in inv):
client = inv['client_mode'] client = inv['client_mode']
logger.info(f"'client_mode' for Monitoring-SN: {inv['monitor_sn']} host: {client['host']}:{client['port']}, forward: {client['forward']}") # noqa: E501 logger.info(f"'client_mode' for Monitoring-SN: {inv['monitor_sn']} host: {client['host']}:{client['port']}, forward: {client['forward']}") # noqa: E501
loop.create_task(self.modbus_loop(client['host'], task = loop.create_task(
client['port'], self.modbus_loop(client['host'],
inv['monitor_sn'], client['port'],
client['forward'])) inv['monitor_sn'],
client['forward']))
self.background_tasks.add(task)
task.add_done_callback(self.background_tasks.discard)
async def modbus_loop(self, host, port, async def modbus_loop(self, host, port,
snr: int, forward: bool) -> None: snr: int, forward: bool) -> None:

View File

@@ -218,6 +218,7 @@ app = Quart(__name__,
static_folder='web/static') static_folder='web/static')
app.secret_key = 'JKLdks.dajlKKKdladkflKwolafallsdfl' app.secret_key = 'JKLdks.dajlKKKdladkflKwolafallsdfl'
app.jinja_env.globals.update(url_for=url_for) app.jinja_env.globals.update(url_for=url_for)
app.background_tasks = set()
server = Server(app, __name__ == "__main__") server = Server(app, __name__ == "__main__")
Web(app, server.trans_path, server.rel_urls) Web(app, server.trans_path, server.rel_urls)
@@ -268,9 +269,13 @@ async def startup_app(): # pragma: no cover
for inv_class, port in [(InverterG3, 5005), (InverterG3P, 10000)]: for inv_class, port in [(InverterG3, 5005), (InverterG3P, 10000)]:
logging.info(f'listen on port: {port} for inverters') logging.info(f'listen on port: {port} for inverters')
loop.create_task(asyncio.start_server(lambda r, w, i=inv_class: task = loop.create_task(
handle_client(r, w, i), asyncio.start_server(lambda r, w, i=inv_class:
'0.0.0.0', port)) handle_client(r, w, i),
'0.0.0.0', port))
app.background_tasks.add(task)
task.add_done_callback(app.background_tasks.discard)
ProxyState.set_up(True) ProxyState.set_up(True)
@@ -294,6 +299,7 @@ async def handle_shutdown(): # pragma: no cover
await inverter.disc(True) await inverter.disc(True)
logging.info('Proxy disconnecting done') logging.info('Proxy disconnecting done')
app.background_tasks.clear()
await Proxy.class_close(loop) await Proxy.class_close(loop)