Skip to content

Commit

Permalink
Added readuntil() exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
alexdelprete committed Feb 13, 2024
1 parent 5a1020e commit 18c3506
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 26 deletions.
73 changes: 51 additions & 22 deletions custom_components/4noks_elios4you/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import asyncio
import logging
import socket
from asyncio import IncompleteReadError, LimitOverrunError
from datetime import datetime

import telnetlib3
Expand Down Expand Up @@ -113,52 +114,62 @@ def check_port(self) -> bool:
if is_open:
sock.shutdown(socket.SHUT_RDWR)
_LOGGER.debug(
f"Check_Port (SUCCESS): port open on {self._host}:{self._port} {datetime.now()}"
f"Check_Port (success): port open on {self._host}:{self._port} {datetime.now()}"
)
else:
_LOGGER.debug(
f"Check_Port (ERROR): port not available on {self._host}:{self._port} - error: {sock_res} {datetime.now()}"
f"Check_Port (error): port not available on {self._host}:{self._port} - error: {sock_res} {datetime.now()}"
)
sock.close()
return is_open

async def async_get_data(self):
"""Read Data Function."""

_LOGGER.debug(f"async_get_data: start async_get_data {datetime.now()}")
_LOGGER.debug(
f"async_get_data (warning): start async_get_data {datetime.now()}"
)
if self.check_port():
try:
reader, writer = await telnetlib3.open_connection(
self._host, self._port
)
_LOGGER.debug(f"async_get_data: start telnet_get_data {datetime.now()}")
_LOGGER.debug(
f"async_get_data (warning): start telnet_get_data {datetime.now()}"
)
dat_parsed = await self.telnet_get_data("@dat", reader, writer)
if dat_parsed is not None:
_LOGGER.debug("async_get_data: parsing @dat data")
for key, value in dat_parsed.items():
# @dat returns only numbers as strings
self.data[key] = round(float(value), 2)
# power/energy as float all others as int
if "Energy" in key or "Power" in key:
self.data[key] = round(float(value), 2)
else:
self.data[key] = int(value)
else:
_LOGGER.debug("async_get_data: @dat data is None")
_LOGGER.debug("async_get_data (error): @dat data is None")

sta_parsed = await self.telnet_get_data("@sta", reader, writer)
if dat_parsed is not None:
_LOGGER.debug("async_get_data: parsing @sta data")
_LOGGER.debug("async_get_data (warning): parsing @sta data")
for key, value in sta_parsed.items():
# @sta returns only numbers as strings
# @sta returns only float numbers as strings
self.data[key] = round(float(value), 2)
else:
_LOGGER.debug("async_get_data: @sta data is None")
_LOGGER.debug("async_get_data (error): @sta data is None")

inf_parsed = await self.telnet_get_data("@inf", reader, writer)
if dat_parsed is not None:
_LOGGER.debug("async_get_data: parsing @inf data")
_LOGGER.debug("async_get_data (warning): parsing @inf data")
for key, value in inf_parsed.items():
# @inf returns only strings
self.data[key] = str(value)
else:
_LOGGER.debug("async_get_data: @inf data is None")
_LOGGER.debug(f"async_get_data: end telnet_get_data {datetime.now()}")
_LOGGER.debug("async_get_data (error): @inf data is None")
_LOGGER.debug(
f"async_get_data (warning): end telnet_get_data {datetime.now()}"
)

# Calculated sensor to combine TOP/BOTTOM fw versions
self.data["swver"] = f"{self.data["fwtop"]} / {self.data["fwbtm"]}"
Expand All @@ -180,14 +191,18 @@ async def async_get_data(self):
)

except TimeoutError:
_LOGGER.debug("async_get_data: Connection or operation timed out")
_LOGGER.debug(
"async_get_data (error): Connection or operation timed out"
)
except Exception as e:
_LOGGER.debug(f"async_get_data: An error occurred: {str(e)}")
_LOGGER.debug(f"async_get_data (error): An error occurred: {str(e)}")
finally:
_LOGGER.debug("async_get_data: closing telnet connection")
_LOGGER.debug("async_get_data (error): closing telnet connection")
reader.feed_eof()
else:
_LOGGER.debug("async_get_data: Elios4you not ready for telnet connection")
_LOGGER.debug(
"async_get_data (error): Elios4you not ready for telnet connection"
)
raise ConnectionError(f"Elios4you not active on {self._host}:{self._port}")
_LOGGER.debug(f"async_get_data: end async_get_data {datetime.now()}")

Expand All @@ -204,17 +219,29 @@ async def telnet_get_data(self, cmd, reader, writer):
writer.write(cmd + "\n")

# read stream up to the "ready..." string
_LOGGER.debug(f"telnet_get_data: readuntil started at {datetime.now()}")
_LOGGER.debug(
f"telnet_get_data (warning): readuntil started at {datetime.now()}"
)
# sometimes telnetlib3 hangs on readuntil so we manage a timeout
try:
async with asyncio.timeout(3):
response = await reader.readuntil(b"ready...")
except IncompleteReadError as ex:
_LOGGER.debug(
f"telnet_get_data (error): Separator not found, chunk exceeded the limit part: {ex.partial} {datetime.now()}"
)
except LimitOverrunError:
_LOGGER.debug(
f"telnet_get_data (error): Separator not found, chunk exceeded the limit {datetime.now()}"
)
except TimeoutError:
_LOGGER.debug(
f"telnet_get_data (exception async): readuntil timed out at {datetime.now()}"
f"telnet_get_data (error): readuntil timed out at {datetime.now()}"
)
finally:
_LOGGER.debug(f"telnet_get_data: readuntil ended at {datetime.now()}")
_LOGGER.debug(
f"telnet_get_data (warning): readuntil ended at {datetime.now()}"
)

# if we had a valid response we process data
if response:
Expand All @@ -239,16 +266,18 @@ async def telnet_get_data(self, cmd, reader, writer):
output[key.lower().replace(" ", "_")] = value.strip()

except ValueError:
_LOGGER.debug(f"Error parsing line: {line}")
_LOGGER.debug(
f"telnet_get_data (error): Error parsing line: {line}"
)
_LOGGER.debug(f"telnet_get_data: success {output}")
else:
_LOGGER.debug("telnet_get_data: response is None")
except TimeoutError:
_LOGGER.debug(
f"telnet_get_data (exception): readuntil timed out at {datetime.now()}"
f"telnet_get_data (error): readuntil timed out at {datetime.now()}"
)
except Exception as ex:
_LOGGER.debug(f"telnet_get_data (exception): failed with error: {ex}")
_LOGGER.debug(f"telnet_get_data (error): failed with error: {ex}")
finally:
return output if response is not None else None

Expand Down
8 changes: 4 additions & 4 deletions custom_components/4noks_elios4you/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,19 +112,19 @@ async def async_turn_on(self, **kwargs):
"""Turn the switch on."""
set_response = await self._coordinator.api.telnet_set_relay("on")
if set_response:
_LOGGER.debug("switch async_turn_on: turned on")
_LOGGER.debug("switch async_turn_on (warning): turned on")
else:
_LOGGER.debug("switch async_turn_on: error turning on")
_LOGGER.debug("switch async_turn_on (error): error turning on")
await self.async_force_update()
return set_response

async def async_turn_off(self, **kwargs):
"""Turn the switch off."""
set_response = await self._coordinator.api.telnet_set_relay("off")
if set_response:
_LOGGER.debug("switch async_turn_on: turned off")
_LOGGER.debug("switch async_turn_off (warning): turned off")
else:
_LOGGER.debug("switch async_turn_on: error turning off")
_LOGGER.debug("switch async_turn_off (error): error turning off")
await self.async_force_update()
return set_response

Expand Down

0 comments on commit 18c3506

Please sign in to comment.