Add the official speedtest cli

When speedtest is executed the first time is prompts for two license acceptances. I opened a terminal as the homeassistant user and accepted the terms before the command worked for me.

I have only seen that once.

Have you been able to run the command from within HA and get some data back?

I keep trying but struggling to get anything - always shows unknown and the logs show empty data errors.

Yes after that it worked.

Please share your config :grinning:

Open a terminal, change to the homeassistant user, run the program and accept the two license agreements:

pi@hassbian:~ $ sudo -u homeassistant -H -s
homeassistant@hassbian:/home/pi $ speedtest --f json-pretty

Configuration.yaml:

- platform: command_line
    name: Speedtest
    command: "speedtest --f json-pretty"
    value_template: "{{ value_json.download['bandwidth'] }}"
    command_timeout: 30
    scan_interval: 1800

Result:

image

2 Likes

I think my issue is related to running Hassio in docker - think it’s an access issue outside of docker that stops the command from running properly. Even using your code I still get nothing back.

I tried running the command as a shell command so that I could pipe the speedtest command and accept the terms and conditions but that didn’t work either.

Shame that the current sensor gives me a result about 50% lower than the CLI command gives.

Glad you got it sorted.

Well I have the same issue on my main HA which runs in docker. This was just a test on a rpi I also have running hassbian.

I tried running console commands in portainer but they would not work. If I run the command speedtest, which uses the built in version, that runs slow as well. Think the main integration needs to be updated. Will raise an issue tomorrow if there isn’t one already.

I use hassio on an RP3 and have the same problem with speedtest.net.
I tried the test with the ethernet port and a gigabit usb adapter, but the download remains below 50 mbit/s (for comparison: i reach 200 mbit/s with my notebook). Can’t test the hint with the terminal and command line, because of hassio (=no hassbian).

The Pi3 shares the ethernet port with USB so it will be slower than FE (100mbit) speeds. The 3B+ can go higher and I was getting the full 200mbit of my connection up to about 3 months ago before it started playing up. My new Pi4 has the same issue so I use the Fast.com integration for my download speed and the Speedtest integration for upload and latency.

1 Like

A bit improved way is to develop a wrapper around the speedtest-cli so that we get output in flat json so that we can read all interesting attributes in single command_line sensor update:

1 Like

Looks pretty good. Do you know how I can make it work in HA docker?

Not sure as I’m using HA Core installation. But here is a post I’ve found on how to access HA command line in docker.
Alternatively you could run the speedtest-cli in the host or another vm etc and run the command_line sensor from there e.g. via ssh e.g. like that:
'sshpass -p "yourpass" ssh -o StrictHostKeyChecking=no user_id@ip_address "python3.8 speedtest_script.py"' - with proper names, paths, python version etc.

Hi @adorobis
i use your script but i have an error with parsing json result of your python script :slight_smile:

Unable to parse output as JSON: Running Speedtest Stdout: {“type”:“result”,“timestamp”:“2022-02-20T09:20:02Z”,“ping”:{“jitter”:0.157,“latency”:2.5649999999999999},“download”:{“bandwidth”:104044380,“bytes”:534511716,“elapsed”:5112},“upload”:{“bandwidth”:73438235,“bytes”:841584580,“elapsed”:12215},“packetLoss”:0,“isp”:“Orange”,“interface”:{“internalIp”:“2a01:cb19:7b9:de00:cf9b:8af5:6490:fdfc”,“name”:“enp0s3”,“macAddr”:“08:00:27:34:06:C2”,“isVpn”:false,“externalIp”:“2a01:cb19:7b9:de00:cf9b:8af5:6490:fdfc”},“server”:{“id”:29542,“host”:“bordeaux3.speedtest.orange.fr”,“port”:8080,“name”:“ORANGE FRANCE”,“location”:“Bordeaux”,“country”:“France”,“ip”:“2a01:cb19:2004:4000::3”},“result”:{“id”:“95f65d94-aa71-4c87-9b91-af11674bf8b0”,“url”:“Speedtest by Ookla - The Global Broadband Speed Test”:true}}

Not sure what’s happening as the above json looks formatted already, instead of "" there are “” characters. Try to run speedtest cli from command line and see if that is what you get. The command that the python script executes is:

speedtest --format=json --precision=4 --server-id=7175 --accept-license --accept-gdpr

and result in my case is:

{"type":"result","timestamp":"2022-02-20T10:09:08Z","ping":{"jitter":1.284,"latency":2.161},"download":{"bandwidth":68744699,"bytes":331946224,"elapsed":4811},"upload":{"bandwidth":14055341,"bytes":214469729,"elapsed":14616},"packetLoss":0,"isp":"Orange Swiatlowod","interface":{"internalIp":"10.144.1.20","name":"em0","macAddr":"68:05:CA:C6:D2:06","isVpn":false,"externalIp":"83.29.138.82"},"server":{"id":7175,"name":"Orange Polska S.A.","location":"Krakow","country":"Poland","host":"kra-o1.speedtest.orange.pl","port":8080,"ip":"80.50.113.170"},"result":{"id":"4602136a-a83d-4a3c-bbf3-0eb8f3521062","url":"https://www.speedtest.net/result/c/4602136a-a83d-4a3c-bbf3-0eb8f3521062"}}

You can then check if that returned result is a proper json (e.g. http://json.parser.online.fr/)

# speedtest-cli ookla bin

sensor:

  - platform: command_line
    name: "Ookla Data"
    command: "/config/apps/speedtest -f json --accept-license --accept-gdpr"
    scan_interval: 1833
    command_timeout: 30
    value_template: >-
        {% set ping = value_json.ping.latency|string %}
        {% set dn = value_json.download.bandwidth|string %}
        {% set up = value_json.upload.bandwidth|string %}
        {{ ping + "," + dn + "," + up }}

template:

  - sensor:

    - name: "Ookla Ping"
      state: >-
        {% set x = states('sensor.ookla_data').split(',')[0] | float(0) %}
        {% if x > 0 %}
          {{ x|round(2) }}
        {% else %}
          {{ states('sensor.ookla_ping') }}
        {% endif %}
      icon: mdi:gauge
      unit_of_measurement: ms
      state_class: measurement

    - name: "Ookla Download"
      state: >-
        {% set x = states('sensor.ookla_data').split(',')[1] | float(0) %}
        {% if x > 0 %}
          {{ (x / 1024 / 1024 * 8)|round(2) }}
        {% else %}
          {{ states('sensor.ookla_download') }}
        {% endif %}
      icon: mdi:gauge
      unit_of_measurement: Mbps
      state_class: measurement

    - name: "Ookla Upload"
      state: >-
        {% set x = states('sensor.ookla_data').split(',')[2] | float(0) %}
        {% if x > 0 %}
          {{ (x / 1024 / 1024 * 8)|round(2) }}
        {% else %}
          {{ states('sensor.ookla_upload') }}
        {% endif %}
      icon: mdi:gauge
      unit_of_measurement: Mbps
      state_class: measurement

#eof

4 Likes

Thank you, I also found the built in integration to be unreliable after I switched to gigabit fiber.
Here is my solution:

sensors.yaml:

# SpeedTest.net CLI
# https://www.speedtest.net/apps/cli
# Template sensors are in configuration.yaml
# Download and test, use binary matching platform, e.g. for x86_64
# wget -qO- https://install.speedtest.net/app/cli/ookla-speedtest-1.2.0-linux-x86_64.tgz | tar xvz
# ./speedtest --accept-license --accept-gdpr
# Copy binary to desired HA config folder
# mkdir -p /config/3rdparty/speedtest
# cp ./speedtest /config/3rdparty/speedtest/
- platform: command_line
  name: "SpeedTest CLI Data"
  unique_id: speedtest_cli_data
  # Use the path as configured on your system
  command: "/config/3rdparty/speedtest/speedtest --format=json --accept-license --accept-gdpr"
  # Every 4 hours, 60 * 60 * 4 = 14400
  scan_interval: 14400
  command_timeout: 60
  # Summarize results to stay below string limit and convert to JSON
  value_template: >-
    {{ 
      { 
        "ping": value_json.ping.latency, 
        "download": value_json.download.bandwidth, 
        "upload": value_json.upload.bandwidth 
      }
      | to_json 
    }}

configuration.yaml:

# SpeedTest.net CLI
# https://www.speedtest.net/apps/cli
# Command_line sensor is in sensors.yaml
template:
  - sensor:
    - name: 'SpeedTest CLI Ping'
      unique_id: speedtest_cli_ping
      icon: mdi:speedometer
      # TIME_MILLISECONDS: Final = "ms"
      unit_of_measurement: ms
      state_class: measurement
      state: "{{ (states('sensor.speedtest_cli_data') | from_json).ping | round(2) }}"
    - name: 'SpeedTest CLI Download'
      unique_id: speedtest_cli_download
      icon: mdi:speedometer
      # DATA_RATE_MEGABITS_PER_SECOND: Final = "Mbit/s"
      unit_of_measurement: Mbit/s
      state_class: measurement
      state: "{{ ((states('sensor.speedtest_cli_data') | from_json).download * 8 / 1000 / 1000) | round(2) }}"
    - name: 'SpeedTest CLI Upload'
      unique_id: speedtest_cli_upload
      icon: mdi:speedometer
      unit_of_measurement: Mbit/s
      state_class: measurement
      state: "{{ ((states('sensor.speedtest_cli_data') | from_json).upload * 8 / 1000 / 1000) | round(2) }}"
12 Likes

Hi, can you let me know what you have placed in your /config/3rdparty/speedtest/ directory ?

Thanks

It is where I extracted the speedtest CLI binary.
https://install.speedtest.net/app/cli/ookla-speedtest-1.1.1-linux-x86_64.tgz

Ah that’s it all working now, many thanks