mirror of
https://github.com/meshtastic/python.git
synced 2026-01-01 12:27:59 -05:00
1.2.29
This commit is contained in:
@@ -154,11 +154,14 @@ import time
|
||||
import base64
|
||||
import platform
|
||||
import socket
|
||||
import timeago
|
||||
from . import mesh_pb2, portnums_pb2, apponly_pb2, admin_pb2, environmental_measurement_pb2, remote_hardware_pb2, channel_pb2, radioconfig_pb2, util
|
||||
from .util import fixme, catchAndIgnore, stripnl, DeferredExecution, Timeout
|
||||
from .node import Node
|
||||
from pubsub import pub
|
||||
from dotmap import DotMap
|
||||
from datetime import datetime
|
||||
from tabulate import tabulate
|
||||
from typing import *
|
||||
from google.protobuf.json_format import MessageToJson
|
||||
|
||||
@@ -247,12 +250,65 @@ class MeshInterface:
|
||||
logging.error(f'Traceback: {traceback}')
|
||||
self.close()
|
||||
|
||||
def showInfo(self):
|
||||
def showInfo(self, file=sys.stdout):
|
||||
"""Show human readable summary about this object"""
|
||||
print(f"My info: {stripnl(MessageToJson(self.myInfo))}")
|
||||
print("\nNodes in mesh:")
|
||||
|
||||
print(f"Owner: {self.getLongName()} ({self.getShortName()})", file=file)
|
||||
print(f"\nMy info: {stripnl(MessageToJson(self.myInfo))}", file=file)
|
||||
print("\nNodes in mesh:", file=file)
|
||||
for n in self.nodes.values():
|
||||
print(" " + stripnl(n))
|
||||
print(f" {stripnl(n)}", file=file)
|
||||
|
||||
def showNodes(self, includeSelf=True, file=sys.stdout):
|
||||
"""Show table summary of nodes in mesh"""
|
||||
def formatFloat(value, precision=2, unit=''):
|
||||
return f'{value:.{precision}f}{unit}' if value else None
|
||||
|
||||
def getLH(ts):
|
||||
return datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') if ts else None
|
||||
|
||||
def getTimeAgo(ts):
|
||||
return timeago.format(datetime.fromtimestamp(ts), datetime.now()) if ts else None
|
||||
|
||||
rows = []
|
||||
for node in self.nodes.values():
|
||||
if not includeSelf and node['num'] == self.localNode.nodeNum:
|
||||
continue
|
||||
|
||||
row = { "N": 0 }
|
||||
|
||||
user = node.get('user')
|
||||
if user:
|
||||
row.update({
|
||||
"User": user['longName'],
|
||||
"AKA": user['shortName'],
|
||||
"ID": user['id'],
|
||||
})
|
||||
|
||||
pos = node.get('position')
|
||||
if pos:
|
||||
row.update({
|
||||
"Latitude": formatFloat(pos.get("latitude"), 4, "°"),
|
||||
"Longitude": formatFloat(pos.get("longitude"), 4, "°"),
|
||||
"Altitude": formatFloat(pos.get("altitude"), 0, " m"),
|
||||
"Battery": formatFloat(pos.get("batteryLevel"), 2, "%"),
|
||||
})
|
||||
|
||||
row.update({
|
||||
"SNR": formatFloat(node.get("snr"), 2, " dB"),
|
||||
"LastHeard": getLH( node.get("lastHeard")),
|
||||
"Since": getTimeAgo( node.get("lastHeard")),
|
||||
})
|
||||
|
||||
rows.append(row)
|
||||
|
||||
# Why doesn't this way work?
|
||||
#rows.sort(key=lambda r: r.get('LastHeard', '0000'), reverse=True)
|
||||
rows.sort(key=lambda r: r.get('LastHeard') or '0000', reverse=True)
|
||||
for i, row in enumerate(rows):
|
||||
row['N'] = i+1
|
||||
|
||||
print(tabulate(rows, headers='keys', missingval='N/A', tablefmt='fancy_grid'), file=file)
|
||||
|
||||
def getNode(self, nodeId):
|
||||
"""Return a node object which contains device settings and channel info"""
|
||||
@@ -771,7 +827,7 @@ class StreamInterface(MeshInterface):
|
||||
self._wantExit = False
|
||||
|
||||
# FIXME, figure out why daemon=True causes reader thread to exit too early
|
||||
self._rxThread = threading.Thread(target=self.__reader, args=())
|
||||
self._rxThread = threading.Thread(target=self.__reader, args=(), daemon=True)
|
||||
|
||||
MeshInterface.__init__(self, debugOut=debugOut, noProto=noProto)
|
||||
|
||||
@@ -1190,6 +1246,7 @@ noProto – If True, don't try to run our protocol on the link - just be a d
|
||||
<li><code><a title="meshtastic.MeshInterface.sendPosition" href="#meshtastic.MeshInterface.sendPosition">sendPosition</a></code></li>
|
||||
<li><code><a title="meshtastic.MeshInterface.sendText" href="#meshtastic.MeshInterface.sendText">sendText</a></code></li>
|
||||
<li><code><a title="meshtastic.MeshInterface.showInfo" href="#meshtastic.MeshInterface.showInfo">showInfo</a></code></li>
|
||||
<li><code><a title="meshtastic.MeshInterface.showNodes" href="#meshtastic.MeshInterface.showNodes">showNodes</a></code></li>
|
||||
<li><code><a title="meshtastic.MeshInterface.waitForConfig" href="#meshtastic.MeshInterface.waitForConfig">waitForConfig</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -1295,12 +1352,65 @@ noProto – If True, don't try to run our protocol on the link - just be a d
|
||||
logging.error(f'Traceback: {traceback}')
|
||||
self.close()
|
||||
|
||||
def showInfo(self):
|
||||
def showInfo(self, file=sys.stdout):
|
||||
"""Show human readable summary about this object"""
|
||||
print(f"My info: {stripnl(MessageToJson(self.myInfo))}")
|
||||
print("\nNodes in mesh:")
|
||||
|
||||
print(f"Owner: {self.getLongName()} ({self.getShortName()})", file=file)
|
||||
print(f"\nMy info: {stripnl(MessageToJson(self.myInfo))}", file=file)
|
||||
print("\nNodes in mesh:", file=file)
|
||||
for n in self.nodes.values():
|
||||
print(" " + stripnl(n))
|
||||
print(f" {stripnl(n)}", file=file)
|
||||
|
||||
def showNodes(self, includeSelf=True, file=sys.stdout):
|
||||
"""Show table summary of nodes in mesh"""
|
||||
def formatFloat(value, precision=2, unit=''):
|
||||
return f'{value:.{precision}f}{unit}' if value else None
|
||||
|
||||
def getLH(ts):
|
||||
return datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') if ts else None
|
||||
|
||||
def getTimeAgo(ts):
|
||||
return timeago.format(datetime.fromtimestamp(ts), datetime.now()) if ts else None
|
||||
|
||||
rows = []
|
||||
for node in self.nodes.values():
|
||||
if not includeSelf and node['num'] == self.localNode.nodeNum:
|
||||
continue
|
||||
|
||||
row = { "N": 0 }
|
||||
|
||||
user = node.get('user')
|
||||
if user:
|
||||
row.update({
|
||||
"User": user['longName'],
|
||||
"AKA": user['shortName'],
|
||||
"ID": user['id'],
|
||||
})
|
||||
|
||||
pos = node.get('position')
|
||||
if pos:
|
||||
row.update({
|
||||
"Latitude": formatFloat(pos.get("latitude"), 4, "°"),
|
||||
"Longitude": formatFloat(pos.get("longitude"), 4, "°"),
|
||||
"Altitude": formatFloat(pos.get("altitude"), 0, " m"),
|
||||
"Battery": formatFloat(pos.get("batteryLevel"), 2, "%"),
|
||||
})
|
||||
|
||||
row.update({
|
||||
"SNR": formatFloat(node.get("snr"), 2, " dB"),
|
||||
"LastHeard": getLH( node.get("lastHeard")),
|
||||
"Since": getTimeAgo( node.get("lastHeard")),
|
||||
})
|
||||
|
||||
rows.append(row)
|
||||
|
||||
# Why doesn't this way work?
|
||||
#rows.sort(key=lambda r: r.get('LastHeard', '0000'), reverse=True)
|
||||
rows.sort(key=lambda r: r.get('LastHeard') or '0000', reverse=True)
|
||||
for i, row in enumerate(rows):
|
||||
row['N'] = i+1
|
||||
|
||||
print(tabulate(rows, headers='keys', missingval='N/A', tablefmt='fancy_grid'), file=file)
|
||||
|
||||
def getNode(self, nodeId):
|
||||
"""Return a node object which contains device settings and channel info"""
|
||||
@@ -2003,7 +2113,7 @@ wantResponse – True if you want the service on the other side to send an a
|
||||
</details>
|
||||
</dd>
|
||||
<dt id="meshtastic.MeshInterface.showInfo"><code class="name flex">
|
||||
<span>def <span class="ident">showInfo</span></span>(<span>self)</span>
|
||||
<span>def <span class="ident">showInfo</span></span>(<span>self, file=sys.stdout)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>Show human readable summary about this object</p></div>
|
||||
@@ -2011,12 +2121,75 @@ wantResponse – True if you want the service on the other side to send an a
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def showInfo(self):
|
||||
<pre><code class="python">def showInfo(self, file=sys.stdout):
|
||||
"""Show human readable summary about this object"""
|
||||
print(f"My info: {stripnl(MessageToJson(self.myInfo))}")
|
||||
print("\nNodes in mesh:")
|
||||
|
||||
print(f"Owner: {self.getLongName()} ({self.getShortName()})", file=file)
|
||||
print(f"\nMy info: {stripnl(MessageToJson(self.myInfo))}", file=file)
|
||||
print("\nNodes in mesh:", file=file)
|
||||
for n in self.nodes.values():
|
||||
print(" " + stripnl(n))</code></pre>
|
||||
print(f" {stripnl(n)}", file=file)</code></pre>
|
||||
</details>
|
||||
</dd>
|
||||
<dt id="meshtastic.MeshInterface.showNodes"><code class="name flex">
|
||||
<span>def <span class="ident">showNodes</span></span>(<span>self, includeSelf=True, file=sys.stdout)</span>
|
||||
</code></dt>
|
||||
<dd>
|
||||
<div class="desc"><p>Show table summary of nodes in mesh</p></div>
|
||||
<details class="source">
|
||||
<summary>
|
||||
<span>Expand source code</span>
|
||||
</summary>
|
||||
<pre><code class="python">def showNodes(self, includeSelf=True, file=sys.stdout):
|
||||
"""Show table summary of nodes in mesh"""
|
||||
def formatFloat(value, precision=2, unit=''):
|
||||
return f'{value:.{precision}f}{unit}' if value else None
|
||||
|
||||
def getLH(ts):
|
||||
return datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') if ts else None
|
||||
|
||||
def getTimeAgo(ts):
|
||||
return timeago.format(datetime.fromtimestamp(ts), datetime.now()) if ts else None
|
||||
|
||||
rows = []
|
||||
for node in self.nodes.values():
|
||||
if not includeSelf and node['num'] == self.localNode.nodeNum:
|
||||
continue
|
||||
|
||||
row = { "N": 0 }
|
||||
|
||||
user = node.get('user')
|
||||
if user:
|
||||
row.update({
|
||||
"User": user['longName'],
|
||||
"AKA": user['shortName'],
|
||||
"ID": user['id'],
|
||||
})
|
||||
|
||||
pos = node.get('position')
|
||||
if pos:
|
||||
row.update({
|
||||
"Latitude": formatFloat(pos.get("latitude"), 4, "°"),
|
||||
"Longitude": formatFloat(pos.get("longitude"), 4, "°"),
|
||||
"Altitude": formatFloat(pos.get("altitude"), 0, " m"),
|
||||
"Battery": formatFloat(pos.get("batteryLevel"), 2, "%"),
|
||||
})
|
||||
|
||||
row.update({
|
||||
"SNR": formatFloat(node.get("snr"), 2, " dB"),
|
||||
"LastHeard": getLH( node.get("lastHeard")),
|
||||
"Since": getTimeAgo( node.get("lastHeard")),
|
||||
})
|
||||
|
||||
rows.append(row)
|
||||
|
||||
# Why doesn't this way work?
|
||||
#rows.sort(key=lambda r: r.get('LastHeard', '0000'), reverse=True)
|
||||
rows.sort(key=lambda r: r.get('LastHeard') or '0000', reverse=True)
|
||||
for i, row in enumerate(rows):
|
||||
row['N'] = i+1
|
||||
|
||||
print(tabulate(rows, headers='keys', missingval='N/A', tablefmt='fancy_grid'), file=file)</code></pre>
|
||||
</details>
|
||||
</dd>
|
||||
<dt id="meshtastic.MeshInterface.waitForConfig"><code class="name flex">
|
||||
@@ -2138,6 +2311,7 @@ debugOut {stream} – If a stream is provided, any debug serial output from
|
||||
<li><code><a title="meshtastic.StreamInterface.sendPosition" href="#meshtastic.MeshInterface.sendPosition">sendPosition</a></code></li>
|
||||
<li><code><a title="meshtastic.StreamInterface.sendText" href="#meshtastic.MeshInterface.sendText">sendText</a></code></li>
|
||||
<li><code><a title="meshtastic.StreamInterface.showInfo" href="#meshtastic.MeshInterface.showInfo">showInfo</a></code></li>
|
||||
<li><code><a title="meshtastic.StreamInterface.showNodes" href="#meshtastic.MeshInterface.showNodes">showNodes</a></code></li>
|
||||
<li><code><a title="meshtastic.StreamInterface.waitForConfig" href="#meshtastic.MeshInterface.waitForConfig">waitForConfig</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -2186,7 +2360,7 @@ debugOut {stream} – If a stream is provided, any debug serial output from
|
||||
self._wantExit = False
|
||||
|
||||
# FIXME, figure out why daemon=True causes reader thread to exit too early
|
||||
self._rxThread = threading.Thread(target=self.__reader, args=())
|
||||
self._rxThread = threading.Thread(target=self.__reader, args=(), daemon=True)
|
||||
|
||||
MeshInterface.__init__(self, debugOut=debugOut, noProto=noProto)
|
||||
|
||||
@@ -2378,6 +2552,7 @@ start the reading thread later.</p></div>
|
||||
<li><code><a title="meshtastic.MeshInterface.sendPosition" href="#meshtastic.MeshInterface.sendPosition">sendPosition</a></code></li>
|
||||
<li><code><a title="meshtastic.MeshInterface.sendText" href="#meshtastic.MeshInterface.sendText">sendText</a></code></li>
|
||||
<li><code><a title="meshtastic.MeshInterface.showInfo" href="#meshtastic.MeshInterface.showInfo">showInfo</a></code></li>
|
||||
<li><code><a title="meshtastic.MeshInterface.showNodes" href="#meshtastic.MeshInterface.showNodes">showNodes</a></code></li>
|
||||
<li><code><a title="meshtastic.MeshInterface.waitForConfig" href="#meshtastic.MeshInterface.waitForConfig">waitForConfig</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -2457,6 +2632,7 @@ hostname {string} – Hostname/IP address of the device to connect to</p></d
|
||||
<li><code><a title="meshtastic.StreamInterface.sendPosition" href="#meshtastic.MeshInterface.sendPosition">sendPosition</a></code></li>
|
||||
<li><code><a title="meshtastic.StreamInterface.sendText" href="#meshtastic.MeshInterface.sendText">sendText</a></code></li>
|
||||
<li><code><a title="meshtastic.StreamInterface.showInfo" href="#meshtastic.MeshInterface.showInfo">showInfo</a></code></li>
|
||||
<li><code><a title="meshtastic.StreamInterface.showNodes" href="#meshtastic.MeshInterface.showNodes">showNodes</a></code></li>
|
||||
<li><code><a title="meshtastic.StreamInterface.waitForConfig" href="#meshtastic.MeshInterface.waitForConfig">waitForConfig</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -2528,6 +2704,7 @@ hostname {string} – Hostname/IP address of the device to connect to</p></d
|
||||
<li><code><a title="meshtastic.MeshInterface.sendPosition" href="#meshtastic.MeshInterface.sendPosition">sendPosition</a></code></li>
|
||||
<li><code><a title="meshtastic.MeshInterface.sendText" href="#meshtastic.MeshInterface.sendText">sendText</a></code></li>
|
||||
<li><code><a title="meshtastic.MeshInterface.showInfo" href="#meshtastic.MeshInterface.showInfo">showInfo</a></code></li>
|
||||
<li><code><a title="meshtastic.MeshInterface.showNodes" href="#meshtastic.MeshInterface.showNodes">showNodes</a></code></li>
|
||||
<li><code><a title="meshtastic.MeshInterface.waitForConfig" href="#meshtastic.MeshInterface.waitForConfig">waitForConfig</a></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -32,7 +32,7 @@ from . import SerialInterface, TCPInterface, BROADCAST_NUM
|
||||
from pubsub import pub
|
||||
import time
|
||||
import sys
|
||||
import threading
|
||||
import threading, traceback
|
||||
from dotmap import DotMap
|
||||
|
||||
"""The interfaces we are using for our tests"""
|
||||
@@ -195,13 +195,18 @@ def testSimulator():
|
||||
"""
|
||||
logging.basicConfig(level=logging.DEBUG if False else logging.INFO)
|
||||
logging.info("Connecting to simulator on localhost!")
|
||||
iface = TCPInterface("localhost")
|
||||
iface.showInfo()
|
||||
iface.localNode.showInfo()
|
||||
iface.localNode.exitSimulator()
|
||||
iface.close()
|
||||
logging.info("Integration test successful!")
|
||||
sys.exit(0)</code></pre>
|
||||
try:
|
||||
iface = TCPInterface("localhost")
|
||||
iface.showInfo()
|
||||
iface.localNode.showInfo()
|
||||
iface.localNode.exitSimulator()
|
||||
iface.close()
|
||||
logging.info("Integration test successful!")
|
||||
sys.exit(0)
|
||||
except:
|
||||
print("Error while testing simulator:", sys.exc_info()[0])
|
||||
traceback.print_exc()
|
||||
sys.exit(1)</code></pre>
|
||||
</details>
|
||||
</section>
|
||||
<section>
|
||||
@@ -453,13 +458,18 @@ python3 -c 'from meshtastic.test import testSimulator; testSimulator()'</p></div
|
||||
"""
|
||||
logging.basicConfig(level=logging.DEBUG if False else logging.INFO)
|
||||
logging.info("Connecting to simulator on localhost!")
|
||||
iface = TCPInterface("localhost")
|
||||
iface.showInfo()
|
||||
iface.localNode.showInfo()
|
||||
iface.localNode.exitSimulator()
|
||||
iface.close()
|
||||
logging.info("Integration test successful!")
|
||||
sys.exit(0)</code></pre>
|
||||
try:
|
||||
iface = TCPInterface("localhost")
|
||||
iface.showInfo()
|
||||
iface.localNode.showInfo()
|
||||
iface.localNode.exitSimulator()
|
||||
iface.close()
|
||||
logging.info("Integration test successful!")
|
||||
sys.exit(0)
|
||||
except:
|
||||
print("Error while testing simulator:", sys.exc_info()[0])
|
||||
traceback.print_exc()
|
||||
sys.exit(1)</code></pre>
|
||||
</details>
|
||||
</dd>
|
||||
<dt id="meshtastic.test.testThread"><code class="name flex">
|
||||
|
||||
2
setup.py
2
setup.py
@@ -12,7 +12,7 @@ with open("README.md", "r") as fh:
|
||||
# This call to setup() does all the work
|
||||
setup(
|
||||
name="meshtastic",
|
||||
version="1.2.25",
|
||||
version="1.2.29",
|
||||
description="Python API & client shell for talking to Meshtastic devices",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
|
||||
Reference in New Issue
Block a user