Can I create a binary sensor that scans a port rather than pinging?

I’ve searched for a few days and I cannot find anyone doing what I want to do, or talking about it anyway, so I thought I would come here for advice.
I have several machines that I am monitoring with home assistant using the binary ping sensor. It works great. However, there is one machine that by design does not reply to pings. I can nmap port 80 and see that it is open. What I would like to do is scan that port every 30-60 seconds and if the port scan shows closed send a notification. Is this possible? I run only home assistant core, btw.

The nmap integration might do what you want.
The docs say that by default it uses nmap’s “fast scan” of the most frequently used 100 ports, with a timeout of 5 seconds.

If that doesn’t work by default, you can use the scan_options setting to scan a specific port.

I looked at that and assumed it was the way to go but I could not find documentation about how to use scan_options. Is it as simple as this?
scan_options: -p 80

It does not.

Have a look at this blog post. Looks to do what you’re after. Great resource for other HA inspiration too :slight_smile:

try

scan_options: -p80

(without the space between “p” and “80)”

Looking at the code for the sensor, the scan_options default is "-F --host-timeout 5s".
If you override this option, it should just pass it as an argument to nmap just the way you would expect.

Thank you Colin, it’ll take me a bit to digest that.
Silicon, I have tried -p80 and -p 80 with and without the -Pn argument (since it doesn’t reply to pings). Still no luck.

It is very useful to study HA code that is available on Gitbub, sometimes.

The default nmap_tracker only tracks devices that it can get MAC address of. If MAC address unavailable for nmap (e.g. host is not on the local network but somewhere in the internet) nmap_tracker won’t “find” the device, although it successfully tests the presence of open port (by specifying correct additional options) on the host/IP address. That’s most likely, by design. :frowning:

In order to use nmap for verifying if host is alive by presence of open port (similarly as PING does) the binary_sensor functionality for nmap needs to be developed.

The usual way to check for a specific port on a specific host is nc (netcat) really, through a command line binary sensor.

Hey koying. I had given up on this. Could you share an example of how this would be done?

Try this one:

binary_sensor:
  - platform: command_line
    name: <sensorName>
    command: "nmap -PS<portToTest> --host-timeout 2s <hostToTest> 2>&1 | grep 'Host is up' > /dev/null && (echo 1) || (echo 0)"
    payload_on: '1'
    payload_off: '0'
    device_class: 'connectivity'
    scan_interval: <inSeconds>

Please note that -PS<portToTest> has no space between parameter and port e.g. -PS80

You can also check this post: Command line binary sensor never switches to on

I did not find a proper documentation on this, just looked at the code: core/binary_sensor.py at ccfe32257e1def92f8c5fbc096af2c1e2bbd9d46 · home-assistant/core · GitHub

What is also extremely convenient about command_line integration is that it is possible to reload only command_line (except for scan_interval for binary_sensor) configuration without restarting whole HA.

3 Likes

Checking every 10 sec (default: 1min) if port 6666 is listening on 192.168.3.245:

binary_sensor:
  - platform: command_line
    name: NC test
    command: 'nc -z 192.168.3.245 6666 > /dev/null 2>&1 && echo success || echo fail'
    device_class: connectivity
    payload_on: "success"
    payload_off: "fail"
    scan_interval: 10
5 Likes

This worked beautifully! Thank you so much!

1 Like

For those in 2024 the code has slightly changed:

command_line:
  - binary_sensor:
      name: Port Forwarding
      command: 'nc -z 192.168.1.173 8123 > /dev/null 2>&1 && echo success || echo fail'
      payload_on: "success"
      payload_off: "fail"
      icon: >
          {% if value == "fail" %} mdi:lan-disconnect
          {% else %} mdi:transit-connection-variant
          {% endif %}
      scan_interval: 900

If you need to check a port on your WAN IP you can swap out the local IP for your duckdns domain:

command: 'nc -z xxxxx.duckdns.org 8123 > /dev/null 2>&1 && echo success || echo fail'

1 Like

Does anyone know why I am getting a short interval of state unknown between on and off using this:

command_line:
  - binary_sensor:
      name: Virgin TV 360
      command: 'nc -z 192.168.0.x 8081 > /dev/null 2>&1 && echo ON || echo OFF'
      icon: mdi:set-top-box
      scan_interval: 30

image

It happens every time.

You need to set the “command_timeout” higher (default is 15 seconds). I set mine to 300 seconds, now it works perfectly without the “unknown” state.