mirror of
https://github.com/meshtastic/python.git
synced 2025-12-26 09:27:52 -05:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0aac077ce7 | ||
|
|
aa6f09635a | ||
|
|
83b0dcad56 | ||
|
|
78d8403bbd | ||
|
|
0813e8dba6 | ||
|
|
23bb2e26f9 | ||
|
|
b59ecff272 | ||
|
|
17f3605736 | ||
|
|
a689fd73a2 | ||
|
|
da30e1141a | ||
|
|
b4bd9568e4 | ||
|
|
aed4f25cf5 | ||
|
|
5c312bedc1 | ||
|
|
428e9a228c | ||
|
|
4500850063 | ||
|
|
aedaa3748d | ||
|
|
bcce5687c5 | ||
|
|
b9d805057f | ||
|
|
d77335caa7 | ||
|
|
b692ef4cfb | ||
|
|
e725292ee0 | ||
|
|
abf9e96d3d | ||
|
|
740f0f0961 | ||
|
|
abb00251c0 | ||
|
|
3335b3d651 |
1
.github/workflows/release.yml
vendored
1
.github/workflows/release.yml
vendored
@@ -52,6 +52,7 @@ jobs:
|
||||
prerelease: true
|
||||
release_name: Meshtastic Python ${{ steps.get_version.outputs.version }}
|
||||
tag_name: ${{ steps.get_version.outputs.version }}
|
||||
commitish: ${{ steps.commit_updated.outputs.sha }}
|
||||
body: |
|
||||
Autogenerated by github action, developer should edit as required before publishing...
|
||||
env:
|
||||
|
||||
@@ -191,6 +191,16 @@ def _onNodeInfoReceive(iface, asDict):
|
||||
iface.nodes[p["id"]] = n
|
||||
_receiveInfoUpdate(iface, asDict)
|
||||
|
||||
def _onTelemetryReceive(iface, asDict):
|
||||
"""Automatically update device metrics on received packets"""
|
||||
logging.debug(f"in _onTelemetryReceive() asDict:{asDict}")
|
||||
deviceMetrics = asDict.get("decoded", {}).get("telemetry", {}).get("deviceMetrics")
|
||||
if "from" in asDict and deviceMetrics is not None:
|
||||
node = iface._getOrCreateByNum(asDict["from"])
|
||||
newMetrics = node.get("deviceMetrics", {})
|
||||
newMetrics.update(deviceMetrics)
|
||||
logging.debug(f"updating metrics for {asDict['from']} to {newMetrics}")
|
||||
node["deviceMetrics"] = newMetrics
|
||||
|
||||
def _receiveInfoUpdate(iface, asDict):
|
||||
if "from" in asDict:
|
||||
@@ -221,7 +231,7 @@ protocols = {
|
||||
portnums_pb2.PortNum.ADMIN_APP: KnownProtocol("admin", admin_pb2.AdminMessage),
|
||||
portnums_pb2.PortNum.ROUTING_APP: KnownProtocol("routing", mesh_pb2.Routing),
|
||||
portnums_pb2.PortNum.TELEMETRY_APP: KnownProtocol(
|
||||
"telemetry", telemetry_pb2.Telemetry
|
||||
"telemetry", telemetry_pb2.Telemetry, _onTelemetryReceive
|
||||
),
|
||||
portnums_pb2.PortNum.REMOTE_HARDWARE_APP: KnownProtocol(
|
||||
"remotehw", remote_hardware_pb2.HardwareMessage
|
||||
|
||||
@@ -314,35 +314,17 @@ def onConnected(interface):
|
||||
print("Setting device position and enabling fixed position setting")
|
||||
# can include lat/long/alt etc: latitude = 37.5, longitude = -122.1
|
||||
interface.localNode.setFixedPosition(lat, lon, alt)
|
||||
elif not args.no_time:
|
||||
# We normally provide a current time to the mesh when we connect
|
||||
if (
|
||||
interface.localNode.nodeNum in interface.nodesByNum
|
||||
and "position" in interface.nodesByNum[interface.localNode.nodeNum]
|
||||
):
|
||||
# send the same position the node already knows, just to update time
|
||||
position = interface.nodesByNum[interface.localNode.nodeNum]["position"]
|
||||
interface.sendPosition(
|
||||
position.get("latitude", 0.0),
|
||||
position.get("longitude", 0.0),
|
||||
position.get("altitude", 0.0),
|
||||
)
|
||||
else:
|
||||
interface.sendPosition()
|
||||
|
||||
if args.set_owner:
|
||||
if args.set_owner or args.set_owner_short:
|
||||
closeNow = True
|
||||
waitForAckNak = True
|
||||
print(f"Setting device owner to {args.set_owner}")
|
||||
interface.getNode(args.dest, False).setOwner(args.set_owner)
|
||||
|
||||
if args.set_owner_short:
|
||||
closeNow = True
|
||||
waitForAckNak = True
|
||||
print(f"Setting device owner short to {args.set_owner_short}")
|
||||
interface.getNode(args.dest, False).setOwner(
|
||||
long_name=None, short_name=args.set_owner_short
|
||||
)
|
||||
if args.set_owner and args.set_owner_short:
|
||||
print(f"Setting device owner to {args.set_owner} and short name to {args.set_owner_short}")
|
||||
elif args.set_owner:
|
||||
print(f"Setting device owner to {args.set_owner}")
|
||||
else: # short name only
|
||||
print(f"Setting device owner short to {args.set_owner_short}")
|
||||
interface.getNode(args.dest, False).setOwner(long_name=args.set_owner, short_name=args.set_owner_short)
|
||||
|
||||
# TODO: add to export-config and configure
|
||||
if args.set_canned_message:
|
||||
@@ -778,7 +760,8 @@ def onConnected(interface):
|
||||
channelIndex = mt_config.channel_index
|
||||
if channelIndex is None:
|
||||
meshtastic.util.our_exit("Warning: Need to specify '--ch-index'.", 1)
|
||||
ch = interface.getNode(args.dest).channels[channelIndex]
|
||||
node = interface.getNode(args.dest)
|
||||
ch = node.channels[channelIndex]
|
||||
|
||||
if args.ch_enable or args.ch_disable:
|
||||
print(
|
||||
@@ -836,7 +819,7 @@ def onConnected(interface):
|
||||
ch.role = channel_pb2.Channel.Role.DISABLED
|
||||
|
||||
print(f"Writing modified channels to device")
|
||||
interface.getNode(args.dest).writeChannel(channelIndex)
|
||||
node.writeChannel(channelIndex)
|
||||
|
||||
if args.get_canned_message:
|
||||
closeNow = True
|
||||
@@ -1466,6 +1449,10 @@ def initParser():
|
||||
|
||||
group.add_argument("--set-owner", help="Set device owner name", action="store")
|
||||
|
||||
group.add_argument(
|
||||
"--set-owner-short", help="Set device owner short name", action="store"
|
||||
)
|
||||
|
||||
group.add_argument(
|
||||
"--set-canned-message",
|
||||
help="Set the canned messages plugin message (up to 200 characters).",
|
||||
@@ -1478,10 +1465,6 @@ def initParser():
|
||||
action="store",
|
||||
)
|
||||
|
||||
group.add_argument(
|
||||
"--set-owner-short", help="Set device owner short name", action="store"
|
||||
)
|
||||
|
||||
group.add_argument(
|
||||
"--set-ham", help="Set licensed Ham ID and turn off encryption", action="store"
|
||||
)
|
||||
@@ -1587,7 +1570,7 @@ def initParser():
|
||||
|
||||
group.add_argument(
|
||||
"--no-time",
|
||||
help="Suppress sending the current time to the mesh",
|
||||
help="Deprecated. Retained for backwards compatibility in scripts, but is a no-op.",
|
||||
action="store_true",
|
||||
)
|
||||
|
||||
|
||||
@@ -442,7 +442,6 @@ class MeshInterface: # pylint: disable=R0902
|
||||
latitude: float = 0.0,
|
||||
longitude: float = 0.0,
|
||||
altitude: int = 0,
|
||||
timeSec: int = 0,
|
||||
destinationId: Union[int, str] = BROADCAST_ADDR,
|
||||
wantAck: bool = False,
|
||||
wantResponse: bool = False,
|
||||
@@ -454,8 +453,6 @@ class MeshInterface: # pylint: disable=R0902
|
||||
Also, the device software will notice this packet and use it to automatically
|
||||
set its notion of the local position.
|
||||
|
||||
If timeSec is not specified (recommended), we will use the local machine time.
|
||||
|
||||
Returns the sent packet. The id field will be populated in this packet and
|
||||
can be used to track future message acks/naks.
|
||||
"""
|
||||
@@ -472,11 +469,6 @@ class MeshInterface: # pylint: disable=R0902
|
||||
p.altitude = int(altitude)
|
||||
logging.debug(f"p.altitude:{p.altitude}")
|
||||
|
||||
if timeSec == 0:
|
||||
timeSec = int(time.time()) # returns unix timestamp in seconds
|
||||
p.time = timeSec
|
||||
logging.debug(f"p.time:{p.time}")
|
||||
|
||||
if wantResponse:
|
||||
onResponse = self.onResponsePosition
|
||||
else:
|
||||
@@ -812,19 +804,22 @@ class MeshInterface: # pylint: disable=R0902
|
||||
lambda: pub.sendMessage("meshtastic.connection.lost", interface=self)
|
||||
)
|
||||
|
||||
def sendHeartbeat(self):
|
||||
"""Sends a heartbeat to the radio. Can be used to verify the connection is healthy."""
|
||||
p = mesh_pb2.ToRadio()
|
||||
p.heartbeat.CopyFrom(mesh_pb2.Heartbeat())
|
||||
self._sendToRadio(p)
|
||||
|
||||
def _startHeartbeat(self):
|
||||
"""We need to send a heartbeat message to the device every X seconds"""
|
||||
|
||||
def callback():
|
||||
self.heartbeatTimer = None
|
||||
i = 300
|
||||
logging.debug(f"Sending heartbeat, interval {i} seconds")
|
||||
if i != 0:
|
||||
self.heartbeatTimer = threading.Timer(i, callback)
|
||||
self.heartbeatTimer.start()
|
||||
p = mesh_pb2.ToRadio()
|
||||
p.heartbeat.CopyFrom(mesh_pb2.Heartbeat())
|
||||
self._sendToRadio(p)
|
||||
interval = 300
|
||||
logging.debug(f"Sending heartbeat, interval {interval} seconds")
|
||||
self.heartbeatTimer = threading.Timer(interval, callback)
|
||||
self.heartbeatTimer.start()
|
||||
self.sendHeartbeat()
|
||||
|
||||
callback() # run our periodic callback now, it will make another timer if necessary
|
||||
|
||||
@@ -975,13 +970,6 @@ class MeshInterface: # pylint: disable=R0902
|
||||
self.localNode.nodeNum = self.myInfo.my_node_num
|
||||
logging.debug(f"Received myinfo: {stripnl(fromRadio.my_info)}")
|
||||
|
||||
failmsg = None
|
||||
|
||||
if failmsg:
|
||||
self.failure = MeshInterface.MeshInterfaceError(failmsg)
|
||||
self.isConnected.set() # let waitConnected return this exception
|
||||
self.close()
|
||||
|
||||
elif fromRadio.HasField("metadata"):
|
||||
self.metadata = fromRadio.metadata
|
||||
logging.debug(f"Received device metadata: {stripnl(fromRadio.metadata)}")
|
||||
|
||||
@@ -5,7 +5,7 @@ import base64
|
||||
import logging
|
||||
import time
|
||||
|
||||
from typing import Union
|
||||
from typing import Optional, Union
|
||||
|
||||
from meshtastic.protobuf import admin_pb2, apponly_pb2, channel_pb2, localonly_pb2, mesh_pb2, portnums_pb2
|
||||
from meshtastic.util import (
|
||||
@@ -279,7 +279,7 @@ class Node:
|
||||
return c.index
|
||||
return 0
|
||||
|
||||
def setOwner(self, long_name=None, short_name=None, is_licensed=False):
|
||||
def setOwner(self, long_name: Optional[str]=None, short_name: Optional[str]=None, is_licensed: bool=False):
|
||||
"""Set device owner name"""
|
||||
logging.debug(f"in setOwner nodeNum:{self.nodeNum}")
|
||||
p = admin_pb2.AdminMessage()
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -324,6 +324,14 @@ class _HardwareModelEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._
|
||||
Heltec Mesh Node T114 board with nRF52840 CPU, and a 1.14 inch TFT display, Ultimate low-power design,
|
||||
specifically adapted for the Meshtatic project
|
||||
"""
|
||||
SENSECAP_INDICATOR: _HardwareModel.ValueType # 70
|
||||
"""
|
||||
Sensecap Indicator from Seeed Studio. ESP32-S3 device with TFT and RP2040 coprocessor
|
||||
"""
|
||||
TRACKER_T1000_E: _HardwareModel.ValueType # 71
|
||||
"""
|
||||
Seeed studio T1000-E tracker card. NRF52840 w/ LR1110 radio, GPS, button, buzzer, and sensors.
|
||||
"""
|
||||
PRIVATE_HW: _HardwareModel.ValueType # 255
|
||||
"""
|
||||
------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -632,6 +640,14 @@ HELTEC_MESH_NODE_T114: HardwareModel.ValueType # 69
|
||||
Heltec Mesh Node T114 board with nRF52840 CPU, and a 1.14 inch TFT display, Ultimate low-power design,
|
||||
specifically adapted for the Meshtatic project
|
||||
"""
|
||||
SENSECAP_INDICATOR: HardwareModel.ValueType # 70
|
||||
"""
|
||||
Sensecap Indicator from Seeed Studio. ESP32-S3 device with TFT and RP2040 coprocessor
|
||||
"""
|
||||
TRACKER_T1000_E: HardwareModel.ValueType # 71
|
||||
"""
|
||||
Seeed studio T1000-E tracker card. NRF52840 w/ LR1110 radio, GPS, button, buzzer, and sensors.
|
||||
"""
|
||||
PRIVATE_HW: HardwareModel.ValueType # 255
|
||||
"""
|
||||
------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -731,6 +747,17 @@ class _CriticalErrorCodeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapp
|
||||
A (likely software but possibly hardware) failure was detected while trying to send packets.
|
||||
If this occurs on your board, please post in the forum so that we can ask you to collect some information to allow fixing this bug
|
||||
"""
|
||||
FLASH_CORRUPTION_RECOVERABLE: _CriticalErrorCode.ValueType # 12
|
||||
"""
|
||||
Corruption was detected on the flash filesystem but we were able to repair things.
|
||||
If you see this failure in the field please post in the forum because we are interested in seeing if this is occurring in the field.
|
||||
"""
|
||||
FLASH_CORRUPTION_UNRECOVERABLE: _CriticalErrorCode.ValueType # 13
|
||||
"""
|
||||
Corruption was detected on the flash filesystem but we were unable to repair things.
|
||||
NOTE: Your node will probably need to be reconfigured the next time it reboots (it will lose the region code etc...)
|
||||
If you see this failure in the field please post in the forum because we are interested in seeing if this is occurring in the field.
|
||||
"""
|
||||
|
||||
class CriticalErrorCode(_CriticalErrorCode, metaclass=_CriticalErrorCodeEnumTypeWrapper):
|
||||
"""
|
||||
@@ -789,6 +816,17 @@ RADIO_SPI_BUG: CriticalErrorCode.ValueType # 11
|
||||
A (likely software but possibly hardware) failure was detected while trying to send packets.
|
||||
If this occurs on your board, please post in the forum so that we can ask you to collect some information to allow fixing this bug
|
||||
"""
|
||||
FLASH_CORRUPTION_RECOVERABLE: CriticalErrorCode.ValueType # 12
|
||||
"""
|
||||
Corruption was detected on the flash filesystem but we were able to repair things.
|
||||
If you see this failure in the field please post in the forum because we are interested in seeing if this is occurring in the field.
|
||||
"""
|
||||
FLASH_CORRUPTION_UNRECOVERABLE: CriticalErrorCode.ValueType # 13
|
||||
"""
|
||||
Corruption was detected on the flash filesystem but we were unable to repair things.
|
||||
NOTE: Your node will probably need to be reconfigured the next time it reboots (it will lose the region code etc...)
|
||||
If you see this failure in the field please post in the forum because we are interested in seeing if this is occurring in the field.
|
||||
"""
|
||||
global___CriticalErrorCode = CriticalErrorCode
|
||||
|
||||
@typing.final
|
||||
|
||||
@@ -185,7 +185,7 @@ def test_sendPosition(caplog):
|
||||
with caplog.at_level(logging.DEBUG):
|
||||
iface.sendPosition()
|
||||
iface.close()
|
||||
assert re.search(r"p.time:", caplog.text, re.MULTILINE)
|
||||
# assert re.search(r"p.time:", caplog.text, re.MULTILINE)
|
||||
|
||||
|
||||
# TODO
|
||||
|
||||
14
poetry.lock
generated
14
poetry.lock
generated
@@ -1,4 +1,4 @@
|
||||
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "altgraph"
|
||||
@@ -1576,13 +1576,13 @@ test = ["jupyter-server (>=2.0.0)", "pytest (>=7.0)", "pytest-jupyter[server] (>
|
||||
|
||||
[[package]]
|
||||
name = "jupyterlab"
|
||||
version = "4.2.3"
|
||||
version = "4.2.5"
|
||||
description = "JupyterLab computational environment"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "jupyterlab-4.2.3-py3-none-any.whl", hash = "sha256:0b59d11808e84bb84105c73364edfa867dd475492429ab34ea388a52f2e2e596"},
|
||||
{file = "jupyterlab-4.2.3.tar.gz", hash = "sha256:df6e46969ea51d66815167f23d92f105423b7f1f06fa604d4f44aeb018c82c7b"},
|
||||
{file = "jupyterlab-4.2.5-py3-none-any.whl", hash = "sha256:73b6e0775d41a9fee7ee756c80f58a6bed4040869ccc21411dc559818874d321"},
|
||||
{file = "jupyterlab-4.2.5.tar.gz", hash = "sha256:ae7f3a1b8cb88b4f55009ce79fa7c06f99d70cd63601ee4aa91815d054f46f75"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -1607,7 +1607,7 @@ dev = ["build", "bump2version", "coverage", "hatch", "pre-commit", "pytest-cov",
|
||||
docs = ["jsx-lexer", "myst-parser", "pydata-sphinx-theme (>=0.13.0)", "pytest", "pytest-check-links", "pytest-jupyter", "sphinx (>=1.8,<7.3.0)", "sphinx-copybutton"]
|
||||
docs-screenshots = ["altair (==5.3.0)", "ipython (==8.16.1)", "ipywidgets (==8.1.2)", "jupyterlab-geojson (==3.4.0)", "jupyterlab-language-pack-zh-cn (==4.1.post2)", "matplotlib (==3.8.3)", "nbconvert (>=7.0.0)", "pandas (==2.2.1)", "scipy (==1.12.0)", "vega-datasets (==0.9.0)"]
|
||||
test = ["coverage", "pytest (>=7.0)", "pytest-check-links (>=0.7)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter (>=0.5.3)", "pytest-timeout", "pytest-tornasync", "requests", "requests-cache", "virtualenv"]
|
||||
upgrade-extension = ["copier (>=8,<10)", "jinja2-time (<0.3)", "pydantic (<2.0)", "pyyaml-include (<2.0)", "tomli-w (<2.0)"]
|
||||
upgrade-extension = ["copier (>=9,<10)", "jinja2-time (<0.3)", "pydantic (<3.0)", "pyyaml-include (<3.0)", "tomli-w (<2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "jupyterlab-pygments"
|
||||
@@ -2287,8 +2287,8 @@ files = [
|
||||
[package.dependencies]
|
||||
numpy = [
|
||||
{version = ">=1.22.4", markers = "python_version < \"3.11\""},
|
||||
{version = ">=1.23.2", markers = "python_version == \"3.11\""},
|
||||
{version = ">=1.26.0", markers = "python_version >= \"3.12\""},
|
||||
{version = ">=1.23.2", markers = "python_version == \"3.11\""},
|
||||
]
|
||||
python-dateutil = ">=2.8.2"
|
||||
pytz = ">=2020.1"
|
||||
@@ -2845,8 +2845,8 @@ astroid = ">=3.2.2,<=3.3.0-dev0"
|
||||
colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
|
||||
dill = [
|
||||
{version = ">=0.2", markers = "python_version < \"3.11\""},
|
||||
{version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""},
|
||||
{version = ">=0.3.7", markers = "python_version >= \"3.12\""},
|
||||
{version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""},
|
||||
]
|
||||
isort = ">=4.2.5,<5.13.0 || >5.13.0,<6"
|
||||
mccabe = ">=0.6,<0.8"
|
||||
|
||||
Submodule protobufs updated: 10494bf328...d0fe91ab99
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "meshtastic"
|
||||
version = "2.4.0a0"
|
||||
version = "2.4.2"
|
||||
description = "Python API & client shell for talking to Meshtastic devices"
|
||||
authors = ["Meshtastic Developers <contact@meshtastic.org>"]
|
||||
license = "GPL-3.0-only"
|
||||
|
||||
Reference in New Issue
Block a user