Use icmplib for ping when available (#39284)
* Use icmplib for ping when available
* Update homeassistant/components/ping/binary_sensor.py
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
* Revert "Update homeassistant/components/ping/binary_sensor.py"
This reverts commit 618f42512a
.
* move it up so its easier to see
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
pull/39344/head
parent
b4bac0f7a0
commit
7c191388a9
|
@ -6,6 +6,7 @@ import re
|
|||
import sys
|
||||
from typing import Any, Dict
|
||||
|
||||
from icmplib import SocketPermissionError, ping as icmp_ping
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.binary_sensor import PLATFORM_SCHEMA, BinarySensorEntity
|
||||
|
@ -59,7 +60,17 @@ def setup_platform(hass, config, add_entities, discovery_info=None) -> None:
|
|||
count = config[CONF_PING_COUNT]
|
||||
name = config.get(CONF_NAME, f"{DEFAULT_NAME} {host}")
|
||||
|
||||
add_entities([PingBinarySensor(name, PingData(host, count))], True)
|
||||
try:
|
||||
# Verify we can create a raw socket, or
|
||||
# fallback to using a subprocess
|
||||
icmp_ping("127.0.0.1", count=0, timeout=0)
|
||||
ping_cls = PingDataICMPLib
|
||||
except SocketPermissionError:
|
||||
ping_cls = PingDataSubProcess
|
||||
|
||||
ping_data = ping_cls(hass, host, count)
|
||||
|
||||
add_entities([PingBinarySensor(name, ping_data)], True)
|
||||
|
||||
|
||||
class PingBinarySensor(BinarySensorEntity):
|
||||
|
@ -102,15 +113,42 @@ class PingBinarySensor(BinarySensorEntity):
|
|||
|
||||
|
||||
class PingData:
|
||||
"""The Class for handling the data retrieval."""
|
||||
"""The base class for handling the data retrieval."""
|
||||
|
||||
def __init__(self, host, count) -> None:
|
||||
def __init__(self, hass, host, count) -> None:
|
||||
"""Initialize the data object."""
|
||||
self.hass = hass
|
||||
self._ip_address = host
|
||||
self._count = count
|
||||
self.data = {}
|
||||
self.available = False
|
||||
|
||||
|
||||
class PingDataICMPLib(PingData):
|
||||
"""The Class for handling the data retrieval using icmplib."""
|
||||
|
||||
def ping(self):
|
||||
"""Send ICMP echo request and return details."""
|
||||
return icmp_ping(self._ip_address, count=self._count)
|
||||
|
||||
async def async_update(self) -> None:
|
||||
"""Retrieve the latest details from the host."""
|
||||
data = await self.hass.async_add_executor_job(self.ping)
|
||||
self.data = {
|
||||
"min": data.min_rtt,
|
||||
"max": data.max_rtt,
|
||||
"avg": data.avg_rtt,
|
||||
"mdev": "",
|
||||
}
|
||||
self.available = data.is_alive
|
||||
|
||||
|
||||
class PingDataSubProcess(PingData):
|
||||
"""The Class for handling the data retrieval using the ping binary."""
|
||||
|
||||
def __init__(self, hass, host, count) -> None:
|
||||
"""Initialize the data object."""
|
||||
super().__init__(hass, host, count)
|
||||
if sys.platform == "win32":
|
||||
self._ping_cmd = [
|
||||
"ping",
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Tracks devices by sending a ICMP echo request (ping)."""
|
||||
|
||||
PING_TIMEOUT = 3
|
||||
PING_ATTEMPTS_COUNT = 3
|
||||
|
|
|
@ -4,6 +4,7 @@ import logging
|
|||
import subprocess
|
||||
import sys
|
||||
|
||||
from icmplib import SocketPermissionError, ping as icmp_ping
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import const, util
|
||||
|
@ -16,7 +17,7 @@ from homeassistant.components.device_tracker.const import (
|
|||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.util.process import kill_subprocess
|
||||
|
||||
from .const import PING_TIMEOUT
|
||||
from .const import PING_ATTEMPTS_COUNT, PING_TIMEOUT
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -31,7 +32,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
|||
)
|
||||
|
||||
|
||||
class Host:
|
||||
class HostSubProcess:
|
||||
"""Host object with ping detection."""
|
||||
|
||||
def __init__(self, ip_address, dev_id, hass, config):
|
||||
|
@ -72,10 +73,46 @@ class Host:
|
|||
_LOGGER.debug("No response from %s failed=%d", self.ip_address, failed)
|
||||
|
||||
|
||||
class HostICMPLib:
|
||||
"""Host object with ping detection."""
|
||||
|
||||
def __init__(self, ip_address, dev_id, _, config):
|
||||
"""Initialize the Host pinger."""
|
||||
self.ip_address = ip_address
|
||||
self.dev_id = dev_id
|
||||
self._count = config[CONF_PING_COUNT]
|
||||
|
||||
def ping(self):
|
||||
"""Send an ICMP echo request and return True if success."""
|
||||
return icmp_ping(self.ip_address, count=PING_ATTEMPTS_COUNT).is_alive
|
||||
|
||||
def update(self, see):
|
||||
"""Update device state by sending one or more ping messages."""
|
||||
if self.ping():
|
||||
see(dev_id=self.dev_id, source_type=SOURCE_TYPE_ROUTER)
|
||||
return True
|
||||
|
||||
_LOGGER.debug(
|
||||
"No response from %s (%s) failed=%d",
|
||||
self.ip_address,
|
||||
self.dev_id,
|
||||
PING_ATTEMPTS_COUNT,
|
||||
)
|
||||
|
||||
|
||||
def setup_scanner(hass, config, see, discovery_info=None):
|
||||
"""Set up the Host objects and return the update function."""
|
||||
|
||||
try:
|
||||
# Verify we can create a raw socket, or
|
||||
# fallback to using a subprocess
|
||||
icmp_ping("127.0.0.1", count=0, timeout=0)
|
||||
host_cls = HostICMPLib
|
||||
except SocketPermissionError:
|
||||
host_cls = HostSubProcess
|
||||
|
||||
hosts = [
|
||||
Host(ip, dev_id, hass, config)
|
||||
host_cls(ip, dev_id, hass, config)
|
||||
for (dev_id, ip) in config[const.CONF_HOSTS].items()
|
||||
]
|
||||
interval = config.get(
|
||||
|
|
|
@ -3,5 +3,6 @@
|
|||
"name": "Ping (ICMP)",
|
||||
"documentation": "https://www.home-assistant.io/integrations/ping",
|
||||
"codeowners": [],
|
||||
"requirements": ["icmplib==1.1.1"],
|
||||
"quality_scale": "internal"
|
||||
}
|
||||
|
|
|
@ -784,6 +784,9 @@ ibm-watson==4.0.1
|
|||
# homeassistant.components.watson_iot
|
||||
ibmiotf==0.3.4
|
||||
|
||||
# homeassistant.components.ping
|
||||
icmplib==1.1.1
|
||||
|
||||
# homeassistant.components.iglo
|
||||
iglo==1.2.7
|
||||
|
||||
|
|
Loading…
Reference in New Issue