ntq
(Nicolas)
September 18, 2023, 10:55am
1
Hi there,
I have defined a few binary sensors to ping some external domains. Unfortunately there seems to be no configuration parameter that allows me to explicitly use IPv6 instead of IPv4.
At the moment the configuration looks like this:
binary_sensor:
- platform: ping
host: vpn.example.com
name: vpn
count: 2
scan_interval: 60
template:
- unique_id: ping
sensor:
- name: "vpn round_trip_time_avg"
state: "{{ state_attr('binary_sensor.vpn', 'round_trip_time_avg') }}"
unit_of_measurement: ms
state_class: measurement
The domain vpn.example.com
has an A and an AAAA record. And it does not seem to be possible to choose one of them. Usually I would just use the argument -6
or -4
on the ping command to choose the correct value. How could I achieve this with the binary_sensor
here?
Of if you know about a better solution than this binary_sensor
-template
-construction with more configuration possibilities, I would really appreciate your ideas.
In case you want to suggest to just use the IP address instead of a domain name: This is not easily possible because there is no static IP address behind that domain. It can change any time.
Thanks!
Unfortunately I don’t have IPv6 support with my current Internet provider, so I cannot test it, but did you already try the Command Line Binary Sensor ?
Something like this:
# Example configuration.yaml entry
command_line:
- binary_sensor:
name: vpn
command: "ping -6 -c 2 vpn.example.com"
ntq
(Nicolas)
September 18, 2023, 6:07pm
3
Thank you for the idea. Is it correct that this is not going to give access to the round-trip-time?
I think I found the source code of the ping
sensor:
dev_id: str,
hass: HomeAssistant,
config: ConfigType,
privileged: bool | None,
) -> None:
"""Initialize the Host pinger."""
self.hass = hass
self.ip_address = ip_address
self.dev_id = dev_id
self._count = config[CONF_PING_COUNT]
self._ping_cmd = ["ping", "-n", "-q", "-c1", "-W1", ip_address]
def ping(self) -> bool | None:
"""Send an ICMP echo request and return True if success."""
with subprocess.Popen(
self._ping_cmd,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
close_fds=False, # required for posix_spawn
) as pinger:
try:
and here:
{
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_PING_COUNT, default=DEFAULT_PING_COUNT): vol.Range(
min=1, max=100
),
}
)
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the Ping Binary sensor."""
host: str = config[CONF_HOST]
count: int = config[CONF_PING_COUNT]
name: str = config.get(CONF_NAME, f"{DEFAULT_NAME} {host}")
privileged: bool | None = hass.data[DOMAIN][PING_PRIVS]
How could I write my own sensor which has more possibilities than this one? Are there tutorials to write my own sensors anywhere?
I am not at all a Linux adept, but as I understand it you essentially can use any Linux option via the command line, so you should be able to access the round-trip-time like that as well?
Anybody else?
1 Like
ntq
(Nicolas)
September 21, 2023, 8:06pm
5
I now created my own ping sensor.
You need this script:
#!/bin/bash
destination="${1:-127.0.0.1}"
function jq::escape() {
echo -n "${1:-}" | jq -Rs
}
address=""
ping_output="$(ping -q -c20 -A -w10 -W2 "${destination}" 2>&1)"
exit_code="$?"
IFS='|' read -r min avg max < <(sed -nr 's/^.*min\/avg\/max\s+=\s+([0-9.]+)\/([0-9.]+)\/([0-9.]+)\sms$/\1|\2|\3/p' <<<"${ping_output}")
address="$(sed -nr 's/^PING\s+[^(]+\(([^)]+)\).*$/\1/p' <<<"${ping_output}")"
IFS='|' read -r transmitted received loss_percentage < <(sed -nr 's/^([0-9]+)\s+packets transmitted,\s+([0-9]+)\s+packets received,\s+([0-9]+)%\s+packet loss$/\1|\2|\3/p' <<<"${ping_output}")
echo '{
"destination": '$(jq::escape "$destination")',
"address": '$(jq::escape "$address")',
"output:": '$(jq::escape "$ping_output")',
"exit_code": '"$exit_code"',
"transmitted": '"${transmitted:-null}"',
"received": '"${received:-null}"',
"loss_percentage": '"${loss_percentage:-null}"',
"min": '"${min:-null}"',
"avg": '"${avg:-null}"',
"max": '"${max:-null}"'
}'
Then you have to define this command_line sensor, and of course you have to correct the path to the script:
command_line:
- sensor:
name: ping_cloudflare
command: /config/user/command_line/ping.sh 1.1.1.1
command_timeout: 30
scan_interval: 60
device_class: duration
state_class: measurement
value_template: "{{ value_json.avg }}"
unit_of_measurement: ms
json_attributes:
- destination
- address
- exit_code
- transmitted
- received
- loss_percentage
- min
- avg
- max
And this template:
template:
- unique_id: ping
sensor:
- name: "ping_cloudflare_min"
state: "{{ state_attr('sensor.ping_cloudflare', 'min') }}"
unit_of_measurement: ms
device_class: duration
state_class: measurement
- name: "ping_cloudflare_max"
state: "{{ state_attr('sensor.ping_cloudflare', 'max') }}"
unit_of_measurement: ms
device_class: duration
state_class: measurement
- name: "ping_cloudflare_loss"
state: "{{ state_attr('sensor.ping_cloudflare', 'loss_percentage') }}"
unit_of_measurement: "%"
state_class: measurement
- name: "ping_cloudflare_transmitted"
state: "{{ state_attr('sensor.ping_cloudflare', 'transmitted') }}"
unit_of_measurement: packets
state_class: measurement
- name: "ping_cloudflare_received"
state: "{{ state_attr('sensor.ping_cloudflare', 'received') }}"
unit_of_measurement: packets
state_class: measurement
There are also other attributes you can use if you need them:
address
: The resolved IP address in case you have used a domain name or hostname
exit_code
: The exit code of the ping command which is usually only 0 if there was no packet loss and everything else went right.
2 Likes
ntq
(Nicolas)
September 21, 2023, 8:07pm
6
I want to make the script more versatile later on. But for now it works good enough.