mirror of
https://github.com/meshtastic/python.git
synced 2025-12-26 09:27:52 -05:00
Merge branch 'meshtastic:master' into master
This commit is contained in:
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -35,7 +35,7 @@ jobs:
|
||||
which meshtastic
|
||||
meshtastic --version
|
||||
- name: Run pylint
|
||||
run: pylint meshtastic examples/
|
||||
run: pylint meshtastic examples/ --ignore-patterns ".*_pb2.py$"
|
||||
- name: Run tests with pytest
|
||||
run: pytest --cov=meshtastic
|
||||
- name: Generate coverage report
|
||||
|
||||
@@ -633,6 +633,10 @@ def onConnected(interface):
|
||||
interface.getNode(args.dest).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')
|
||||
else:
|
||||
print("Showing info of remote node is not supported.")
|
||||
print("Use the '--get' command for a specific configuration (e.g. 'lora') instead.")
|
||||
|
||||
@@ -5,6 +5,7 @@ import random
|
||||
import time
|
||||
import json
|
||||
import logging
|
||||
import collections
|
||||
from typing import AnyStr
|
||||
import threading
|
||||
from datetime import datetime
|
||||
@@ -56,6 +57,8 @@ class MeshInterface:
|
||||
self.configId = None
|
||||
self.gotResponse = False # used in gpio read
|
||||
self.mask = None # used in gpio read and gpio watch
|
||||
self.queueStatus = None
|
||||
self.queue = collections.OrderedDict()
|
||||
|
||||
def close(self):
|
||||
"""Shutdown this interface"""
|
||||
@@ -509,13 +512,61 @@ class MeshInterface:
|
||||
m.disconnect = True
|
||||
self._sendToRadio(m)
|
||||
|
||||
def _queueHasFreeSpace(self):
|
||||
# We never got queueStatus, maybe the firmware is old
|
||||
if self.queueStatus is None:
|
||||
return True
|
||||
return self.queueStatus.free > 0
|
||||
|
||||
def _queueClaim(self):
|
||||
if self.queueStatus is None:
|
||||
return
|
||||
self.queueStatus.free -= 1
|
||||
|
||||
def _sendToRadio(self, toRadio):
|
||||
"""Send a ToRadio protobuf to the device"""
|
||||
if self.noProto:
|
||||
logging.warning(f"Not sending packet because protocol use is disabled by noProto")
|
||||
else:
|
||||
#logging.debug(f"Sending toRadio: {stripnl(toRadio)}")
|
||||
self._sendToRadioImpl(toRadio)
|
||||
|
||||
if not toRadio.HasField('packet'):
|
||||
# not a meshpacket -- send immediately, give queue a chance,
|
||||
# this makes heartbeat trigger queue
|
||||
self._sendToRadioImpl(toRadio)
|
||||
else:
|
||||
# meshpacket -- queue
|
||||
self.queue[toRadio.packet.id] = toRadio
|
||||
|
||||
resentQueue = collections.OrderedDict()
|
||||
|
||||
while self.queue:
|
||||
#logging.warn("queue: " + " ".join(f'{k:08x}' for k in self.queue))
|
||||
while not self._queueHasFreeSpace():
|
||||
logging.debug("Waiting for free space in TX Queue")
|
||||
time.sleep(0.5)
|
||||
try:
|
||||
toResend = self.queue.popitem(last=False)
|
||||
except KeyError:
|
||||
break
|
||||
packetId, packet = toResend
|
||||
#logging.warn(f"packet: {packetId:08x} {packet}")
|
||||
resentQueue[packetId] = packet
|
||||
if packet is False:
|
||||
continue
|
||||
self._queueClaim()
|
||||
if packet != toRadio:
|
||||
logging.debug(f"Resending packet ID {packetId:08x} {packet}")
|
||||
self._sendToRadioImpl(packet)
|
||||
|
||||
#logging.warn("resentQueue: " + " ".join(f'{k:08x}' for k in resentQueue))
|
||||
for packetId, packet in resentQueue.items():
|
||||
if self.queue.pop(packetId, False) is False: # Packet got acked under us
|
||||
logging.debug(f"packet {packetId:08x} got acked under us")
|
||||
continue
|
||||
if packet:
|
||||
self.queue[packetId] = packet
|
||||
#logging.warn("queue + resentQueue: " + " ".join(f'{k:08x}' for k in self.queue))
|
||||
|
||||
def _sendToRadioImpl(self, toRadio):
|
||||
"""Send a ToRadio protobuf to the device"""
|
||||
@@ -528,6 +579,21 @@ class MeshInterface:
|
||||
"""
|
||||
self.localNode.requestChannels()
|
||||
|
||||
def _handleQueueStatusFromRadio(self, queueStatus):
|
||||
self.queueStatus = queueStatus
|
||||
logging.debug(f"TX QUEUE free {queueStatus.free} of {queueStatus.maxlen}, res = {queueStatus.res}, id = {queueStatus.mesh_packet_id:08x} ")
|
||||
|
||||
if queueStatus.res:
|
||||
return
|
||||
|
||||
#logging.warn("queue: " + " ".join(f'{k:08x}' for k in self.queue))
|
||||
justQueued = self.queue.pop(queueStatus.mesh_packet_id, None)
|
||||
|
||||
if justQueued is None and queueStatus.mesh_packet_id != 0:
|
||||
self.queue[queueStatus.mesh_packet_id] = False
|
||||
logging.debug(f"Reply for unexpected packet ID {queueStatus.mesh_packet_id:08x}")
|
||||
#logging.warn("queue: " + " ".join(f'{k:08x}' for k in self.queue))
|
||||
|
||||
def _handleFromRadio(self, fromRadioBytes):
|
||||
"""
|
||||
Handle a packet that arrived from the radio(update model and publish events)
|
||||
@@ -584,6 +650,9 @@ class MeshInterface:
|
||||
elif fromRadio.HasField("packet"):
|
||||
self._handlePacketFromRadio(fromRadio.packet)
|
||||
|
||||
elif fromRadio.HasField('queueStatus'):
|
||||
self._handleQueueStatusFromRadio(fromRadio.queueStatus)
|
||||
|
||||
elif fromRadio.rebooted:
|
||||
# Tell clients the device went away. Careful not to call the overridden
|
||||
# subclass version that closes the serial port
|
||||
|
||||
@@ -14,6 +14,8 @@ import subprocess
|
||||
import serial
|
||||
import serial.tools.list_ports
|
||||
import pkg_resources
|
||||
import requests
|
||||
|
||||
|
||||
from meshtastic.supported_device import supported_devices
|
||||
|
||||
@@ -241,7 +243,11 @@ def support_info():
|
||||
print(f' Encoding (stdin): {sys.stdin.encoding}')
|
||||
print(f' Encoding (stdout): {sys.stdout.encoding}')
|
||||
the_version = pkg_resources.get_distribution("meshtastic").version
|
||||
print(f' meshtastic: v{the_version}')
|
||||
pypi_version = check_if_newer_version()
|
||||
if pypi_version:
|
||||
print(f' meshtastic: v{the_version} (*** newer version v{pypi_version} available ***)')
|
||||
else:
|
||||
print(f' meshtastic: v{the_version}')
|
||||
print(f' Executable: {sys.argv[0]}')
|
||||
print(f' Python: {platform.python_version()} {platform.python_implementation()} {platform.python_compiler()}')
|
||||
print('')
|
||||
@@ -545,3 +551,19 @@ def detect_windows_port(sd):
|
||||
#print(f'x:{x}')
|
||||
ports.add(f'COM{x}')
|
||||
return ports
|
||||
|
||||
|
||||
def check_if_newer_version():
|
||||
"""Check pip to see if we are running the latest version."""
|
||||
pypi_version = None
|
||||
try:
|
||||
url = "https://pypi.org/pypi/meshtastic/json"
|
||||
data = requests.get(url).json()
|
||||
pypi_version = data["info"]["version"]
|
||||
except Exception as e:
|
||||
#print(f"could not get version from pypi e:{e}")
|
||||
pass
|
||||
act_version = pkg_resources.get_distribution("meshtastic").version
|
||||
if pypi_version and pkg_resources.parse_version(pypi_version) <= pkg_resources.parse_version(act_version):
|
||||
return None
|
||||
return pypi_version
|
||||
|
||||
@@ -7,6 +7,7 @@ pyqrcode
|
||||
tabulate
|
||||
timeago
|
||||
webencodings
|
||||
requests
|
||||
pyparsing
|
||||
twine
|
||||
autopep8
|
||||
|
||||
2
setup.py
2
setup.py
@@ -30,7 +30,7 @@ setup(
|
||||
],
|
||||
packages=["meshtastic"],
|
||||
include_package_data=True,
|
||||
install_requires=["pyserial>=3.4", "protobuf>=3.13.0",
|
||||
install_requires=["pyserial>=3.4", "protobuf>=3.13.0", "requests>=2.25.0",
|
||||
"pypubsub>=4.0.3", "dotmap>=1.3.14", "pexpect>=4.6.0", "pyqrcode>=1.2.1",
|
||||
"tabulate>=0.8.9", "timeago>=1.0.15", "pyyaml",
|
||||
"pygatt>=4.0.5 ; platform_system=='Linux'"],
|
||||
|
||||
Reference in New Issue
Block a user