From a9997eeee46df089874cf1835cb9f0ea39c97e35 Mon Sep 17 00:00:00 2001 From: David Rapan Date: Tue, 10 Dec 2024 11:09:47 +0100 Subject: [PATCH] refactor: Add InverterState for handling connection state --- custom_components/solarman/api.py | 52 +++++++++++++-------- custom_components/solarman/binary_sensor.py | 4 +- custom_components/solarman/sensor.py | 2 +- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/custom_components/solarman/api.py b/custom_components/solarman/api.py index 2b69ac5..7ed7fa3 100644 --- a/custom_components/solarman/api.py +++ b/custom_components/solarman/api.py @@ -110,27 +110,44 @@ async def write_multiple_holding_registers(self, register_addr, values): return await super().write_multiple_holding_registers(register_addr, values) return await self._tcp_parse_response_adu(write_multiple_registers(self.mb_slave_id, register_addr, values)) +class InverterState(): + def __init__(self): + self.updated = datetime.now() + self.updated_interval = 0 + self.value = -1 + + @property + def print(self): + return "Connected" if self.value > 0 else "Disconnected" + + def update(self, reinit: bool = False): + now = datetime.now() + if reinit: + self.updated = now + else: + self.updated_interval = now - self.updated + self.updated = now + self.value = 1 + + def reevaluate(self) -> int: + self.value = 0 if self.value == 1 else -1 + return self.value == -1 + class Inverter(): def __init__(self, config: ConfigurationProvider): self._semaphore = asyncio.Semaphore(1) self._write_lock = True - self.state = -1 - self.state_interval = 0 - self.state_updated = datetime.now() + self.state: InverterState = InverterState() self.config: ConfigurationProvider = config self.endpoint: EndPointProvider = None self.profile: ProfileProvider = None self.modbus: PySolarmanV5AsyncEthernetWrapper = None - self.device_info = {} + self.device_info: dict = {} @property def available(self): - return self.state > -1 - - @property - def get_connection_state(self): - return "Connected" if self.state > 0 else "Disconnected" + return self.state.value > -1 async def load(self): try: @@ -150,12 +167,12 @@ def check(self, lock): raise UserWarning("Entity is locked!") async def shutdown(self) -> None: - self.state = -1 + self.state.value = -1 await self.modbus.disconnect() async def read_write(self, code, start, arg): if await self.modbus.connect(): - self.state_updated = datetime.now() + self.state.update(True) match code: case CODE.READ_COILS: @@ -218,16 +235,13 @@ async def get(self, runtime = 0, requests = None): result = self.profile.parser.process(responses) if not requests else responses - if (rc := len(result) if result else 0) > 0 and (now := datetime.now()): - _LOGGER.debug(f"[{self.config.serial}] Returning {rc} new values to the Coordinator. [Previous State: {self.get_connection_state} ({self.state})]") - self.state_interval = now - self.state_updated - self.state_updated = now - self.state = 1 + if (rc := len(result) if result else 0) > 0: + _LOGGER.debug(f"[{self.config.serial}] Returning {rc} new values to the Coordinator. [Previous State: {self.state.print} ({self.state.value})]") + self.state.update() except (TimeoutError, Exception) as e: - _LOGGER.debug(f"[{self.config.serial}] Fetching failed. [Previous State: {self.get_connection_state} ({self.state})]") - self.state = 0 if self.state == 1 else -1 - if self.state == -1: + _LOGGER.debug(f"[{self.config.serial}] Fetching failed. [Previous State: {self.state.print} ({self.state.value})]") + if self.state.reevaluate(): await self.modbus.disconnect() raise _LOGGER.debug(f"[{self.config.serial}] {"Timeout" if isinstance(e, TimeoutError) else "Error"} fetching {self.config.name} data: {format_exception(e)}") diff --git a/custom_components/solarman/binary_sensor.py b/custom_components/solarman/binary_sensor.py index 022179e..2ee1264 100644 --- a/custom_components/solarman/binary_sensor.py +++ b/custom_components/solarman/binary_sensor.py @@ -72,6 +72,6 @@ def is_on(self) -> bool | None: return self._attr_state > -1 def update(self): - self.set_state(self.coordinator.inverter.state) - self._attr_extra_state_attributes["updated"] = self.coordinator.inverter.state_updated.strftime("%m/%d/%Y, %H:%M:%S") + self.set_state(self.coordinator.inverter.state.value) + self._attr_extra_state_attributes["updated"] = self.coordinator.inverter.state.updated.strftime("%m/%d/%Y, %H:%M:%S") # Maybe set the timestamp using HA's datetime format??? diff --git a/custom_components/solarman/sensor.py b/custom_components/solarman/sensor.py index 91e53be..1026e69 100644 --- a/custom_components/solarman/sensor.py +++ b/custom_components/solarman/sensor.py @@ -84,7 +84,7 @@ def available(self) -> bool: return self._attr_native_value > 0 def update(self): - self.set_state(self.coordinator.inverter.state_interval.total_seconds()) + self.set_state(self.coordinator.inverter.state.updated_interval.total_seconds()) class SolarmanSensor(SolarmanSensorEntity): def __init__(self, coordinator, sensor):