Meshtastic Python CLI checks for an upgrade each time it is run

I recently started experimenting with several Meshtastic nodes based on the popular SX1262 radio and ESP32 microcontroller. If you don't want to use your phone to control them over the bluetooth connection you can use WiFi or serial-via-USB. To do this you use the meshtastic python library which is also a command line tool.

One common function is to query the entire status of the Meshtastic node. You can do this by running meshtastic --info or in my case meshtastic --tcp 192.168.162.160 --info since my node is available over my local network. This displays a dump of all the configuration and state of the node. Here's an example of what that looks like

$ meshtastic --tcp 192.168.162.160 --info
Connected to radio

Owner: hydrogen18fixed (h18f)
My info: { "myNodeNum": 1658397140, "rebootCount": 44, "minAppVersion": 30200, "deviceId": "wzonC4GmNlc0H7J/5Zjsgg==", "pioEnv": "tlora-t3s3-v1", "firmwareEdition": "VANILLA", "nodedbCount": 0 }

When I started experimenting I just grabbed the latest Meshtastic version and flashed it to my nodes, as well as the latest Meshtastic python tool. What puzzled me was sometimes the --info command seemed to take extremely long to run. I figured this might be some issue with communicating with the node but it was hard to reproduce. Yesterday I noticed this little message after my expected output

*** A newer version v2.7.3 is available! Consider running "pip install --upgrade meshtastic" ***

The command line tool for an off-grid mesh networking software package somehow knows it has an update available. How is that possible? Well, Meshtastic is an open source package so we can check the source code. In the file meshtastic/__main__.py from the linked repository we can find this

# originally from meshtastic/__main__.py line 980

if args.info:
    print("")
    # If we aren't trying to talk to our local node, don't show it
    if args.dest == BROADCAST_ADDR:
        interface.showInfo()
        print("")
        interface.getNode(args.dest, **getNode_kwargs).showInfo()
        closeNow = True
        print("")
        pypi_version = meshtastic.util.check_if_newer_version()
        if pypi_version:
            print(
                f"*** A newer version v{pypi_version} is available!"
                ' Consider running "pip install --upgrade meshtastic" ***\n'
            )

The statement if args.info checks if the --info command is being requested by the user. Sure enough it calls meshtastic.util.check_if_newer_version(). So let's look at that function

# originally from meshtastic/util.py line 659
def check_if_newer_version() -> Optional[str]:
    """Check pip to see if we are running the latest version."""
    pypi_version: Optional[str] = None
    try:
        url: str = "https://pypi.org/pypi/meshtastic/json"
        data = requests.get(url, timeout=5).json()
        pypi_version = data["info"]["version"]
    except Exception:
        pass
    act_version = get_active_version()

    if pypi_version is None:
        return None
    try:
        parsed_act_version = pkg_version.parse(act_version)
        parsed_pypi_version = pkg_version.parse(pypi_version)
        #Note: if handed "None" when we can't download the pypi_version,
        #this gets a TypeError:
        #"TypeError: expected string or bytes-like object, got 'NoneType'"
        #Handle that below?
    except pkg_version.InvalidVersion:
        return pypi_version

    if parsed_pypi_version <= parsed_act_version:
        return None

    return pypi_version

This function sends an HTTP GET request using the python requests module to the URL "https://pypi.org/pypi/meshtastic/json". It then parses that as JSON and does some kind of version check. I was curious to see what this endpoint returns so I just downloaded it myself using curl

$ curl "https://pypi.org/pypi/meshtastic/json" > meshtastic_version.json
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  380k  100  380k    0     0  2479k      0 --:--:-- --:--:-- --:--:-- 2489k
$ ls -l meshtastic_version.json 
-rw-rw-r-- 1 ericu ericu 390049 Sep 19 08:02 meshtastic_version.json

So this endpoint returns 380.9 kilobytes of data on each request, just to check for a new version. I wanted to make sure I wasn't misunderstanding this code so I blocked internet connectivity from my local machine and ran the command again

$ time meshtastic --tcp 192.168.162.160 --info
Connected to radio

Owner: hydrogen18fixed (h18f)

...SNIP...

Channels:
  Index 0: PRIMARY psk=default { "psk": "AQ==", "uplinkEnabled": true, "downlinkEnabled": true, "moduleSettings": { "positionPrecision": 13, "isClientMuted": false }, "channelNum": 0, "name": "", "id": 0 }

Primary channel URL: https://meshtastic.org/e/#CgsSAQEoATABOgIIDRIMCAE4AUADSAFQHmgB


real    0m16.186s
user    0m0.292s
sys 0m0.036s

This time the version upgrade message does not appear! It also takes 16 seconds to run. Most of which most time the tool is just sitting there after having already printed all the data I asked for. So at this point what we have is an open-source off grid messaging software package that includes a mandatory version check on each invocation of the --info command. It then proceeds to download 380.9 kilobytes of data just to check if the latest version is newer than the current one. There is no way to disable this. It gets worse however because the python requests module is used to download this data. This means if you installed into a Python virtual environment, you downloaded the requests module as part of the install. After searching through the code this appears to be the only usage of that module. I think this is one of the best examples of how not implementing a feature could improve the product function.


Copyright Eric Urban 2025, or the respective entity where indicated