Ping binary sensor for ipv6

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"

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:

and here:

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

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

I want to make the script more versatile later on. But for now it works good enough.