1 """Ping classes shared between platforms."""
4 from contextlib
import suppress
7 from typing
import TYPE_CHECKING, Any
9 from icmplib
import NameLookupError, async_ping
13 from .const
import ICMP_TIMEOUT, PING_TIMEOUT
15 _LOGGER = logging.getLogger(__name__)
17 PING_MATCHER = re.compile(
18 r"(?P<min>\d+.\d+)\/(?P<avg>\d+.\d+)\/(?P<max>\d+.\d+)\/(?P<mdev>\d+.\d+)"
21 PING_MATCHER_BUSYBOX = re.compile(
22 r"(?P<min>\d+.\d+)\/(?P<avg>\d+.\d+)\/(?P<max>\d+.\d+)"
25 WIN32_PING_MATCHER = re.compile(
r"(?P<min>\d+)ms.+(?P<max>\d+)ms.+(?P<avg>\d+)ms")
29 """The base class for handling the data retrieval."""
31 data: dict[str, Any] |
None =
None
32 is_alive: bool =
False
34 def __init__(self, hass: HomeAssistant, host: str, count: int) ->
None:
35 """Initialize the data object."""
42 """The Class for handling the data retrieval using icmplib."""
45 self, hass: HomeAssistant, host: str, count: int, privileged: bool |
None
47 """Initialize the data object."""
52 """Retrieve the latest details from the host."""
53 _LOGGER.debug(
"ping address: %s", self.
ip_addressip_address)
55 data = await async_ping(
61 except NameLookupError:
62 _LOGGER.debug(
"Error resolving host: %s", self.
ip_addressip_address)
67 "async_ping returned: reachable=%s sent=%i received=%s",
70 data.packets_received,
73 self.
is_aliveis_alive = data.is_alive
86 """The Class for handling the data retrieval using the ping binary."""
89 self, hass: HomeAssistant, host: str, count: int, privileged: bool |
None
91 """Initialize the data object."""
104 """Send ICMP echo request and return details if success."""
109 pinger = await asyncio.create_subprocess_exec(
112 stdout=asyncio.subprocess.PIPE,
113 stderr=asyncio.subprocess.PIPE,
117 async
with asyncio.timeout(self.
_count_count + PING_TIMEOUT):
118 out_data, out_error = await pinger.communicate()
122 "Output of command: `%s`, return code: %s:\n%s",
129 "Error of command: `%s`, return code: %s:\n%s",
135 if pinger.returncode
and pinger.returncode > 1:
138 "Error running command: `%s`, return code: %s",
143 if "max/" not in str(out_data):
144 match = PING_MATCHER_BUSYBOX.search(
145 str(out_data).rsplit(
"\n", maxsplit=1)[-1]
148 assert match
is not None
149 rtt_min, rtt_avg, rtt_max = match.groups()
150 return {
"min": rtt_min,
"avg": rtt_avg,
"max": rtt_max}
151 match = PING_MATCHER.search(
str(out_data).rsplit(
"\n", maxsplit=1)[-1])
153 assert match
is not None
154 rtt_min, rtt_avg, rtt_max, rtt_mdev = match.groups()
157 "Timed out running command: `%s`, after: %s",
159 self.
_count_count + PING_TIMEOUT,
163 with suppress(TypeError):
168 except AttributeError
as err:
169 _LOGGER.debug(
"Error matching ping output: %s", err)
171 return {
"min": rtt_min,
"avg": rtt_avg,
"max": rtt_max,
"mdev": rtt_mdev}
174 """Retrieve the latest details from the host."""
None __init__(self, HomeAssistant hass, str host, int count, bool|None privileged)
None __init__(self, HomeAssistant hass, str host, int count, bool|None privileged)
dict[str, Any]|None async_ping(self)
None __init__(self, HomeAssistant hass, str host, int count)