mirror of
https://github.com/meshtastic/python.git
synced 2026-01-20 05:37:57 -05:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c049d3424a | ||
|
|
471535853b | ||
|
|
676148cc14 | ||
|
|
a915b05240 | ||
|
|
a1668e8c66 | ||
|
|
e7664cb40b | ||
|
|
83c18f4008 | ||
|
|
8b6321ce7f | ||
|
|
9fac981ba6 | ||
|
|
ccc71930f7 | ||
|
|
9380f048fa |
@@ -68,8 +68,7 @@ class MeshInterface:
|
|||||||
|
|
||||||
def __exit__(self, exc_type, exc_value, traceback):
|
def __exit__(self, exc_type, exc_value, traceback):
|
||||||
if exc_type is not None and exc_value is not None:
|
if exc_type is not None and exc_value is not None:
|
||||||
logging.error(
|
logging.error(f'An exception of type {exc_type} with value {exc_value} has occurred')
|
||||||
f'An exception of type {exc_type} with value {exc_value} has occurred')
|
|
||||||
if traceback is not None:
|
if traceback is not None:
|
||||||
logging.error(f'Traceback: {traceback}')
|
logging.error(f'Traceback: {traceback}')
|
||||||
self.close()
|
self.close()
|
||||||
@@ -84,10 +83,10 @@ class MeshInterface:
|
|||||||
nodes = ""
|
nodes = ""
|
||||||
if self.nodes:
|
if self.nodes:
|
||||||
for n in self.nodes.values():
|
for n in self.nodes.values():
|
||||||
# when the TBeam is first booted, it sometimes shows the 'raw' data
|
# when the TBeam is first booted, it sometimes shows the raw data
|
||||||
# so, we will just remove any raw keys
|
# so, we will just remove any raw keys
|
||||||
n2 = remove_keys_from_dict('raw', n)
|
keys_to_remove = ('raw', 'decoded', 'payload')
|
||||||
n2 = remove_keys_from_dict('decode', n2)
|
n2 = remove_keys_from_dict(keys_to_remove, n)
|
||||||
|
|
||||||
# if we have 'macaddr', re-format it
|
# if we have 'macaddr', re-format it
|
||||||
if 'macaddr' in n2['user']:
|
if 'macaddr' in n2['user']:
|
||||||
@@ -394,11 +393,11 @@ class MeshInterface:
|
|||||||
return user.get('shortName', None)
|
return user.get('shortName', None)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _waitConnected(self):
|
def _waitConnected(self, timeout=15.0):
|
||||||
"""Block until the initial node db download is complete, or timeout
|
"""Block until the initial node db download is complete, or timeout
|
||||||
and raise an exception"""
|
and raise an exception"""
|
||||||
if not self.noProto:
|
if not self.noProto:
|
||||||
if not self.isConnected.wait(15.0): # timeout after x seconds
|
if not self.isConnected.wait(timeout): # timeout after x seconds
|
||||||
raise Exception("Timed out waiting for connection completion")
|
raise Exception("Timed out waiting for connection completion")
|
||||||
|
|
||||||
# If we failed while connecting, raise the connection to the client
|
# If we failed while connecting, raise the connection to the client
|
||||||
@@ -416,8 +415,7 @@ class MeshInterface:
|
|||||||
def _disconnected(self):
|
def _disconnected(self):
|
||||||
"""Called by subclasses to tell clients this interface has disconnected"""
|
"""Called by subclasses to tell clients this interface has disconnected"""
|
||||||
self.isConnected.clear()
|
self.isConnected.clear()
|
||||||
publishingThread.queueWork(lambda: pub.sendMessage(
|
publishingThread.queueWork(lambda: pub.sendMessage("meshtastic.connection.lost", interface=self))
|
||||||
"meshtastic.connection.lost", interface=self))
|
|
||||||
|
|
||||||
def _startHeartbeat(self):
|
def _startHeartbeat(self):
|
||||||
"""We need to send a heartbeat message to the device every X seconds"""
|
"""We need to send a heartbeat message to the device every X seconds"""
|
||||||
@@ -443,8 +441,7 @@ class MeshInterface:
|
|||||||
if not self.isConnected.is_set():
|
if not self.isConnected.is_set():
|
||||||
self.isConnected.set()
|
self.isConnected.set()
|
||||||
self._startHeartbeat()
|
self._startHeartbeat()
|
||||||
publishingThread.queueWork(lambda: pub.sendMessage(
|
publishingThread.queueWork(lambda: pub.sendMessage("meshtastic.connection.established", interface=self))
|
||||||
"meshtastic.connection.established", interface=self))
|
|
||||||
|
|
||||||
def _startConfig(self):
|
def _startConfig(self):
|
||||||
"""Start device packets flowing"""
|
"""Start device packets flowing"""
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ from ..mesh_interface import MeshInterface
|
|||||||
from ..node import Node
|
from ..node import Node
|
||||||
from .. import mesh_pb2
|
from .. import mesh_pb2
|
||||||
from ..__init__ import LOCAL_ADDR, BROADCAST_ADDR
|
from ..__init__ import LOCAL_ADDR, BROADCAST_ADDR
|
||||||
|
from ..radioconfig_pb2 import RadioConfig
|
||||||
|
from ..util import Timeout
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -164,6 +166,22 @@ def test_sendPosition(reset_globals, caplog):
|
|||||||
assert re.search(r'p.time:', caplog.text, re.MULTILINE)
|
assert re.search(r'p.time:', caplog.text, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_close_with_heartbeatTimer(reset_globals, caplog):
|
||||||
|
"""Test close() with heartbeatTimer"""
|
||||||
|
iface = MeshInterface(noProto=True)
|
||||||
|
anode = Node('foo', 'bar')
|
||||||
|
radioConfig = RadioConfig()
|
||||||
|
radioConfig.preferences.phone_timeout_secs = 10
|
||||||
|
anode.radioConfig = radioConfig
|
||||||
|
iface.localNode = anode
|
||||||
|
assert iface.heartbeatTimer is None
|
||||||
|
with caplog.at_level(logging.DEBUG):
|
||||||
|
iface._startHeartbeat()
|
||||||
|
assert iface.heartbeatTimer is not None
|
||||||
|
iface.close()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_handleFromRadio_empty_payload(reset_globals, caplog):
|
def test_handleFromRadio_empty_payload(reset_globals, caplog):
|
||||||
"""Test _handleFromRadio"""
|
"""Test _handleFromRadio"""
|
||||||
@@ -543,3 +561,70 @@ def test_getOrCreateByNum(capsys, reset_globals, iface_with_nodes):
|
|||||||
iface.myInfo.my_node_num = 2475227164
|
iface.myInfo.my_node_num = 2475227164
|
||||||
tmp = iface._getOrCreateByNum(2475227164)
|
tmp = iface._getOrCreateByNum(2475227164)
|
||||||
assert tmp['num'] == 2475227164
|
assert tmp['num'] == 2475227164
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_enter():
|
||||||
|
"""Test __enter__()"""
|
||||||
|
iface = MeshInterface(noProto=True)
|
||||||
|
assert iface == iface.__enter__()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_exit_with_exception(caplog):
|
||||||
|
"""Test __exit__()"""
|
||||||
|
iface = MeshInterface(noProto=True)
|
||||||
|
with caplog.at_level(logging.ERROR):
|
||||||
|
iface.__exit__('foo', 'bar', 'baz')
|
||||||
|
assert re.search(r'An exception of type foo with value bar has occurred', caplog.text, re.MULTILINE)
|
||||||
|
assert re.search(r'Traceback: baz', caplog.text, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_showNodes_exclude_self(capsys, caplog, reset_globals, iface_with_nodes):
|
||||||
|
"""Test that we hit that continue statement"""
|
||||||
|
with caplog.at_level(logging.DEBUG):
|
||||||
|
iface = iface_with_nodes
|
||||||
|
iface.localNode.nodeNum = 2475227164
|
||||||
|
iface.showNodes()
|
||||||
|
iface.showNodes(includeSelf=False)
|
||||||
|
capsys.readouterr()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_waitForConfig(caplog, capsys):
|
||||||
|
"""Test waitForConfig()"""
|
||||||
|
iface = MeshInterface(noProto=True)
|
||||||
|
# override how long to wait
|
||||||
|
iface._timeout = Timeout(0.01)
|
||||||
|
with pytest.raises(Exception) as pytest_wrapped_e:
|
||||||
|
iface.waitForConfig()
|
||||||
|
assert pytest_wrapped_e.type == Exception
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'Exception: Timed out waiting for interface config', err, re.MULTILINE)
|
||||||
|
assert out == ''
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_waitConnected_raises_an_exception(caplog, capsys):
|
||||||
|
"""Test waitConnected()"""
|
||||||
|
iface = MeshInterface(noProto=True)
|
||||||
|
with pytest.raises(Exception) as pytest_wrapped_e:
|
||||||
|
iface.failure = "warn about something"
|
||||||
|
iface._waitConnected(0.01)
|
||||||
|
assert pytest_wrapped_e.type == Exception
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'warn about something', err, re.MULTILINE)
|
||||||
|
assert out == ''
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_waitConnected_isConnected_timeout(caplog, capsys):
|
||||||
|
"""Test waitConnected()"""
|
||||||
|
with pytest.raises(Exception) as pytest_wrapped_e:
|
||||||
|
iface = MeshInterface()
|
||||||
|
iface._waitConnected(0.01)
|
||||||
|
assert pytest_wrapped_e.type == Exception
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert re.search(r'warn about something', err, re.MULTILINE)
|
||||||
|
assert out == ''
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from ..serial_interface import SerialInterface
|
|||||||
from ..admin_pb2 import AdminMessage
|
from ..admin_pb2 import AdminMessage
|
||||||
from ..channel_pb2 import Channel
|
from ..channel_pb2 import Channel
|
||||||
from ..radioconfig_pb2 import RadioConfig
|
from ..radioconfig_pb2 import RadioConfig
|
||||||
|
from ..util import Timeout
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -90,6 +91,16 @@ def test_setOwner_no_short_name_and_long_name_has_words(caplog):
|
|||||||
assert re.search(r'p.set_owner.team:0', caplog.text, re.MULTILINE)
|
assert re.search(r'p.set_owner.team:0', caplog.text, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_setOwner_long_name_no_short(caplog):
|
||||||
|
"""Test setOwner"""
|
||||||
|
anode = Node('foo', 'bar', noProto=True)
|
||||||
|
with caplog.at_level(logging.DEBUG):
|
||||||
|
anode.setOwner(long_name ='Aabo', is_licensed=True)
|
||||||
|
assert re.search(r'p.set_owner.long_name:Aabo:', caplog.text, re.MULTILINE)
|
||||||
|
assert re.search(r'p.set_owner.short_name:Aab:', caplog.text, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_exitSimulator(caplog):
|
def test_exitSimulator(caplog):
|
||||||
"""Test exitSimulator"""
|
"""Test exitSimulator"""
|
||||||
@@ -869,3 +880,14 @@ def test_onResponseRequestSetting_with_error(capsys):
|
|||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert re.search(r'Error on response', out)
|
assert re.search(r'Error on response', out)
|
||||||
assert err == ''
|
assert err == ''
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_waitForConfig():
|
||||||
|
"""Test waitForConfig()"""
|
||||||
|
anode = Node('foo', 'bar')
|
||||||
|
radioConfig = RadioConfig()
|
||||||
|
anode.radioConfig = radioConfig
|
||||||
|
anode._timeout = Timeout(0.01)
|
||||||
|
result = anode.waitForConfig()
|
||||||
|
assert not result
|
||||||
|
|||||||
@@ -184,6 +184,23 @@ def test_remove_keys_from_dict():
|
|||||||
assert remove_keys_from_dict(('b'), {'a':1, 'b':2}) == {'a':1}
|
assert remove_keys_from_dict(('b'), {'a':1, 'b':2}) == {'a':1}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_remove_keys_from_dict_multiple_keys():
|
||||||
|
"""Test remove_keys_from_dict()"""
|
||||||
|
keys = ('a', 'b')
|
||||||
|
adict = {'a': 1, 'b': 2, 'c': 3}
|
||||||
|
assert remove_keys_from_dict(keys, adict) == {'c':3}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_remove_keys_from_dict_nested():
|
||||||
|
"""Test remove_keys_from_dict()"""
|
||||||
|
keys = ('b')
|
||||||
|
adict = {'a': {'b': 1}, 'b': 2, 'c': 3}
|
||||||
|
exp = {'a': {}, 'c': 3}
|
||||||
|
assert remove_keys_from_dict(keys, adict) == exp
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unitslow
|
@pytest.mark.unitslow
|
||||||
def test_Timeout_not_found():
|
def test_Timeout_not_found():
|
||||||
"""Test Timeout()"""
|
"""Test Timeout()"""
|
||||||
|
|||||||
@@ -204,12 +204,18 @@ def support_info():
|
|||||||
|
|
||||||
|
|
||||||
def remove_keys_from_dict(keys, adict):
|
def remove_keys_from_dict(keys, adict):
|
||||||
"""Return a dictionary without some keys in it."""
|
"""Return a dictionary without some keys in it.
|
||||||
newdict = adict
|
Will removed nested keys.
|
||||||
|
"""
|
||||||
for key in keys:
|
for key in keys:
|
||||||
if key in adict:
|
try:
|
||||||
del newdict[key]
|
del adict[key]
|
||||||
return newdict
|
except:
|
||||||
|
pass
|
||||||
|
for val in adict.values():
|
||||||
|
if isinstance(val, dict):
|
||||||
|
remove_keys_from_dict(keys, val)
|
||||||
|
return adict
|
||||||
|
|
||||||
|
|
||||||
def hexstr(barray):
|
def hexstr(barray):
|
||||||
|
|||||||
2
proto
2
proto
Submodule proto updated: 1d3b4806ab...7b80bde421
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
|
# This call to setup() does all the work
|
||||||
setup(
|
setup(
|
||||||
name="meshtastic",
|
name="meshtastic",
|
||||||
version="1.2.49",
|
version="1.2.51",
|
||||||
description="Python API & client shell for talking to Meshtastic devices",
|
description="Python API & client shell for talking to Meshtastic devices",
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
|
|||||||
Reference in New Issue
Block a user